<template>
  <div v-loading="pageLoading" class="app-container no-margin aisopchat-tree-selected">
    <el-row :gutter="20" class="main-container">
      <!-- 左侧面板 - 交互命令 -->
      <el-col :span="8" class="left-panel">
        <div class="chat-panel">
          <!-- 模型选择器 -->
          <div class="model-selector-container">
            <!-- 当前选择的模型显示 -->
            <div class="model-selector-header">
              <div class="selected-model-display">
                <div v-if="currentModel" class="selected-model">
                  <i class="el-icon-cpu" />
                  <span class="model-name">{{ currentModel.name }}</span>
                  <el-tag v-if="currentModel.type" size="mini" type="info">{{ currentModel.type }}</el-tag>
                </div>
                <div v-else class="placeholder-text">
                  <i class="el-icon-s-operation" />
                  <span>请选择AI模型</span>
                </div>
                <el-button
                  :icon="treeCollapsed ? 'el-icon-arrow-down' : 'el-icon-arrow-up'"
                  size="mini"
                  type="text"
                  class="tree-toggle-btn"
                  @click="toggleTreeCollapse"
                >
                  {{ treeCollapsed ? '展开' : '收起' }}
                </el-button>
              </div>
            </div>

            <!-- 树形选择器区域 -->
            <div v-show="!treeCollapsed" class="tree-selector-content">
              <div class="tree-header">
                <el-input
                  v-model="filterText"
                  placeholder="搜索模型"
                  clearable
                  prefix-icon="el-icon-search"
                  size="small"
                  style="margin-bottom: 10px;"
                />
                <el-button
                  icon="el-icon-refresh"
                  size="mini"
                  style="float: right; margin-bottom: 10px;"
                  :loading="pageLoading"
                  @click="refreshTreeData"
                >
                  刷新
                </el-button>
              </div>
              <el-tree
                ref="treeRef"
                style="font-size: 13px; max-height: 300px; overflow-y: auto;"
                :data="treeData"
                accordion
                size="mini"
                :props="{ label: 'name', children: 'children' }"
                :filter-node-method="filterNode"
                highlight-current
                :default-checked-keys="checkedKeys"
                :check-strictly="true"
                node-key="id"
                :render-content="renderContent"
                class="model-tree"
                @node-click="handleNodeClick"
                @node-expand="handleNodeExpand"
                @node-collapse="handleNodeCollapse"
                @check="handleCheck"
              />
            </div>
          </div>

          <!-- 交互命令区域 -->
          <div class="chat-input-section">
            <!-- 主要内容区域（可滚动） -->
            <div class="chat-content-area">
              <div class="chat-input" tabindex="-1">
                <!--              <div class="message-count-info" v-if="chatMessages.length > 0">-->
                <!--                <span>当前对话消息数: {{ chatMessages.length }}/{{ maxMessagesPerConversation }}</span>-->
                <!--                <el-progress-->
                <!--                  :percentage="Math.round(chatMessages.length / maxMessagesPerConversation * 100)"-->
                <!--                  :status="chatMessages.length >= maxMessagesPerConversation * 0.9 ? 'warning' : ''"-->
                <!--                  :stroke-width="10"-->
                <!--                  class="message-count-progress"-->
                <!--                ></el-progress>-->
                <!--              </div>-->
                <!-- 显示AI默认参考素材 - 带勾选功能 -->
                <div v-if="nodeReferences.length > 0" class="node-references-section">
                  <div class="section-title">
                    <span>AI默认参考素材 (单选)</span>
                    <div class="section-actions">
                      <el-button
                        v-if="nodeReferences.length > 12"
                        size="mini"
                        type="primary"
                        icon="el-icon-more"
                        @click="openReferenceDialog"
                      >
                        更多 ({{ nodeReferences.length }})
                      </el-button>
                      <el-button
                        v-if="selectedReferences.length > 0"
                        size="mini"
                        type="text"
                        @click="clearSelectedReferences"
                      >
                        取消选择
                      </el-button>
                    </div>
                  </div>
                  <div class="node-references-grid">
                    <div
                      v-for="reference in displayedReferences"
                      :key="reference.id"
                      class="reference-grid-item"
                      :class="{ 'selected': isReferenceSelected(reference) }"
                      :title="reference.filename || '未命名文件'"
                      @click="toggleReferenceSelection(reference)"
                      @mouseenter="handleReferenceHover(reference, $event)"
                      @mouseleave="hideImagePreview"
                    >
                      <!-- 文件预览 -->
                      <div class="file-preview">
                        <div v-if="isImageFile(reference.filename)" class="image-thumbnail">
                          <img :src="reference.file_url" :alt="reference.filename" @error="handleImageError">
                        </div>
                        <div v-else class="file-icon-large">
                          <i v-if="isExcelFile(reference.filename)" class="el-icon-document" style="color: #27ae60;" />
                          <i v-else-if="isPDFFile(reference.filename)" class="el-icon-document" style="color: #e74c3c;" />
                          <i v-else class="el-icon-document" style="color: #606266;" />
                        </div>
                      </div>
                    </div>
                  </div>
                </div>

                <!-- 显示已上传文件列表 - 移到输入框上方 -->
                <div v-if="uploadedFiles.length > 0" class="uploaded-files-grid">
                  <div v-for="file in uploadedFiles" :key="file.uid" class="file-grid-item">
                    <!-- 勾选框 -->
                    <div class="file-checkbox">
                      <el-checkbox v-model="file.selected" size="small" />
                    </div>
                    <!-- 图片缩略图或文件图标 -->
                    <div
                      class="file-preview"
                      @mouseenter="handleFileHover(file, $event)"
                      @mouseleave="hideImagePreview"
                    >
                      <div v-if="isImageFile(file.file_name)" class="image-thumbnail">
                        <img :src="file.file_url" :alt="getShortFileName(file)" @error="handleImageError">
                      </div>
                      <div v-else class="file-icon-large">
                        <i v-if="isExcelFile(file.file_name)" class="el-icon-document" />
                        <i v-else class="el-icon-document" />
                      </div>
                    </div>
                    <!-- 文件名 - 所有文件都显示简化名称 -->
                    <div class="file-name-small" :title="file.display_name || file.original_name">
                      {{ getShortFileName(file) }}
                    </div>
                    <!-- 操作按钮 - hover时显示 -->
                    <div class="file-actions-hover">
                      <i v-if="isImageFile(file.file_name)" class="el-icon-view" title="预览" @click="previewImage(file.file_url)" />
                      <i class="el-icon-delete" title="删除" @click="removeFile(file.uid)" />
                    </div>
                  </div>
                </div>
              </div>
            </div>

            <!-- 图片生成参数选择器 - 仅在图片类型时显示 -->
            <div v-if="currentModel && (currentModel.ai_type === 'image' || currentModel.ai_type === 'comprehensive')" class="image-params-section">
              <div class="params-controls">
                <div class="param-item">
                  <label>宽高比:</label>
                  <el-select v-model="imageParams.aspectRatio" placeholder="选择宽高比" size="mini" clearable>
                    <el-option label="1:1" value="1:1" />
                    <el-option label="16:9" value="16:9" />
                    <el-option label="9:16" value="9:16" />
                    <el-option label="2:3" value="2:3" />
                    <el-option label="3:2" value="3:2" />
                    <el-option label="3:4" value="3:4" />
                    <el-option label="4:3" value="4:3" />
                    <el-option label="4:5" value="4:5" />
                    <el-option label="5:4" value="5:4" />
                    <el-option label="21:9" value="21:9" />
                  </el-select>
                </div>
                <div class="param-item">
                  <label>分辨率:</label>
                  <el-select v-model="imageParams.resolution" placeholder="选择分辨率" size="mini" clearable>
                    <el-option label="1K" value="1K" />
                    <el-option label="2K" value="2K" />
                    <el-option label="4K" value="4K" />
                  </el-select>
                </div>
              </div>
            </div>

            <!-- 输入区域（固定在底部） -->
            <div class="chat-input-area">
              <AIChatInput
                ref="chatInput"
                v-model="inputMessage"
                :min-height="120"
                :max-height="400"
                :placeholder="placeholder"
                :disabled="!currentModel"
                :auto-height="true"
                @send="sendMessage"
                @image-uploaded="handleImageUploaded"
              />

              <div class="input-actions">
                <!-- 第一行：操作按钮 -->
                <div class="action-row">
                  <div class="left-actions">
                    <el-upload
                      class="upload-button"
                      :action="upload_url"
                      :headers="uploadHeaders"
                      :show-file-list="false"
                      :accept="'.jpg,.jpeg,.png,.webp,.xlsx,.txt,.zip'"
                      :before-upload="beforeUpload"
                      :on-success="handleUploadSuccess"
                      :on-error="handleUploadError"
                      :data="uploadData"
                      :disabled="!currentModel"
                      name="file"
                      :multiple="false"
                    >
                      <el-button
                        size="small"
                        :disabled="!currentModel"
                        type="text"
                        icon="el-icon-upload2"
                      >
                        上传文件
                      </el-button>
                    </el-upload>

                    <!-- 添加URL按钮 -->
                    <el-button
                      size="small"
                      :disabled="!currentModel"
                      type="text"
                      icon="el-icon-link"
                      @click="showUrlDialog"
                    >
                      添加URL
                    </el-button>

                    <!-- 优化提示词按钮 -->
                    <el-button
                      size="small"
                      :disabled="!currentModel || !inputMessage.trim()"
                      type="text"
                      icon="el-icon-magic-stick"
                      :loading="optimizingPrompt"
                      style="color: #1EB7E6;"
                      @click="optimizePrompt"
                    >
                      优化提示词
                    </el-button>
                  </div>
                  <div class="right-actions">
                    <el-button
                      v-if="uploadedFiles.length > 0"
                      size="small"
                      type="warning"
                      icon="el-icon-delete"
                      @click="clearAllFiles"
                    >
                      清空附件
                    </el-button>
                  </div>
                </div>
                <!-- 第二行：提示文字和发送按钮 -->
                <div class="send-row">
                  <span class="input-tip">按Ctrl+Enter发送</span>
                  <el-button
                    type="primary"
                    :loading="sendingMessage"
                    :disabled="(!inputMessage.trim() && uploadedFiles.length === 0 && selectedReferences.length === 0) || !currentModel"
                    @click="sendMessage"
                  >
                    发送
                  </el-button>
                </div>
              </div>
            </div>
          </div>
        </div>
      </el-col>

      <!-- 右侧面板 - AI消息对话窗口 -->
      <el-col :span="16" class="right-panel">
        <div class="chat-container">
          <!-- 顶部工具栏 -->
          <div class="chat-header">
            <div class="conversation-controls">
              <div class="header-content">
                <span class="model-name">{{ currentModel ? currentModel.name : '请选择节点' }}</span>
                <div class="conversation-actions">
                  <el-button
                    circle
                    size="mini"
                    icon="el-icon-share"
                    :disabled="!currentModel"
                    title="分享节点"
                    @click="shareCurrentNode"
                  />
                  <el-button
                    circle
                    size="mini"
                    icon="el-icon-plus"
                    :disabled="!currentModel"
                    title="新会话"
                    @click="createNewConversation"
                  />
                  <el-button
                    circle
                    size="mini"
                    icon="el-icon-time"
                    title="历史会话"
                    @click="openHistoryDrawer"
                  />
                  <el-button
                    circle
                    size="mini"
                    icon="el-icon-setting"
                    title="设置"
                    @click="openSettingsDialog"
                  />
                </div>
              </div>
            </div>
          </div>

          <!-- AI消息区域 -->
          <div ref="chatContainerRef" class="chat-messages">
            <template v-if="chatMessages.length">
              <div
                v-for="(message, index) in chatMessages"
                :key="index"
                :class="['message', message.role]"
              >
                <div v-if="message.role === 'user'" class="message-avatar">
                  <svg-icon icon-class="user" />
                </div>
                <div class="message-body">
                  <div v-if="message.role !== 'system'" class="message-info">
                    <span class="message-sender">{{ message.role === 'user' ? '您' : (currentModel ? currentModel.name : 'AI助手') }}</span>
                    <span class="message-time">{{ message.time }}</span>
                  </div>
                  <div
                    v-if="message.role === 'user'"
                    class="message-content user-message-content"
                    v-html="message.content"
                  />
                  <!-- 显示用户消息中的附件 -->
                  <div v-if="message.files && message.files.length > 0" class="message-files-container">
                    <div class="message-files-list">
                      <div v-for="file in message.files" :key="file.uid" class="message-file-item">
                        <!-- 图片显示缩略图，其他文件显示图标 -->
                        <div v-if="isImageFile(file.file_name)" class="file-thumbnail" @click="previewImage(file.file_url)">
                          <img :src="file.file_url" :alt="file.display_name || file.original_name" class="thumbnail-image" @error="handleImageError">
                        </div>
                        <div v-else class="file-icon">
                          <i v-if="isExcelFile(file.file_name)" class="el-icon-document" />
                          <i v-else class="el-icon-document" />
                        </div>
                        <div class="file-info">
                          <div class="file-name">{{ file.display_name || file.original_name }}</div>
                        </div>
                        <div class="file-actions">
                          <el-button v-if="isImageFile(file.file_name)" type="text" size="small" @click="previewImage(file.file_url)">预览</el-button>
                          <el-button v-else type="text" size="small" @click="downloadFile(file.file_url, file.original_name)">下载</el-button>
                        </div>
                      </div>
                    </div>
                  </div>

                  <!-- 助手消息 -->
                  <div
                    v-else-if="message.role === 'assistant'"
                    class="message-content"
                    v-html="renderMarkdown(message.content)"
                  />

                  <!-- 系统消息 -->
                  <div
                    v-else-if="message.role === 'system'"
                    class="message-content system-message-content"
                    v-html="message.content"
                  />

                  <!-- 用户消息的操作按钮 -->
                  <div v-if="message.role === 'user'" class="message-actions">
                    <el-button
                      type="text"
                      size="mini"
                      class="copy-btn"
                      title="复制内容"
                      @click="copyMessageContent(message.content, false)"
                    >
                      <i class="el-icon-document-copy" />复制
                    </el-button>
                  </div>
                </div>
                <div v-if="message.role === 'assistant'" class="message-actions">
                  <el-button
                    type="text"
                    size="mini"
                    class="copy-btn"
                    title="复制内容"
                    @click="copyMessageContent(message.content, true)"
                  >
                    <i class="el-icon-document-copy" />复制
                  </el-button>
                </div>
                <div v-if="message.role === 'assistant'" class="message-avatar">
                  <svg-icon icon-class="robot" />
                </div>
              </div>
            </template>

            <!-- AI思考中提示 - 显示在消息底部 -->
            <div v-if="loading" class="loading-message-right">
              <div class="loading-content">
                <i class="el-icon-loading" />
                <span>AI正在思考中...</span>
                <el-button size="mini" type="danger" class="cancel-btn" @click="cancelRequest">
                  <svg-icon icon-class="stop" />
                </el-button>
              </div>
            </div>

            <div v-else class="empty-chat">
              <div class="empty-chat-content">
                <svg-icon icon-class="chat" class="empty-icon" />
                <p>选择一个SOP节点并开始对话</p>
              </div>
            </div>
          </div>

          <!-- 🆕 连续对话输入框（固定在右下角） -->
          <div v-if="currentModel" class="continuous-chat-input-area">
            <div class="continuous-input-header">
              <i class="el-icon-chat-dot-round" />
              <span class="input-mode-label">连续对话</span>
              <el-tooltip content="在此输入可保持对话上下文，不会清空历史记录" placement="top">
                <i class="el-icon-question" style="margin-left: 5px; color: #909399; cursor: help;" />
              </el-tooltip>
            </div>
            <AIChatInput
              ref="continuousChatInput"
              v-model="continuousInputMessage"
              :min-height="60"
              :max-height="200"
              placeholder="继续对话（保持历史上下文）..."
              :disabled="!currentModel"
              :auto-height="true"
              @send="sendContinuousMessage"
              @image-uploaded="handleContinuousImageUploaded"
            />
            <div class="continuous-input-actions">
              <span class="input-tip">按Ctrl+Enter发送</span>
              <el-button
                type="primary"
                size="small"
                :loading="sendingMessage"
                :disabled="!continuousInputMessage.trim() || !currentModel"
                @click="sendContinuousMessage"
              >
                发送
              </el-button>
            </div>
          </div>
        </div>
      </el-col>
    </el-row>

    <!-- 历史会话抽屉 -->
    <el-drawer
      title="历史会话"
      :visible.sync="historyDrawerVisible"
      direction="rtl"
      size="30%"
    >
      <div class="history-drawer-content">
        <div v-if="conversations.length === 0 && !historyLoading" class="empty-history">
          <svg-icon icon-class="chat" class="empty-icon" />
          <p>暂无历史会话</p>
        </div>
        <div v-else>
          <div class="history-search">
            <el-input
              v-model="historySearchText"
              placeholder="搜索会话"
              clearable
              prefix-icon="el-icon-search"
              @clear="handleHistorySearch"
              @keyup.enter.native="handleHistorySearch"
            >
              <el-button slot="append" icon="el-icon-search" @click="handleHistorySearch" />
            </el-input>
          </div>
          <div class="history-actions">
            <el-button
              type="danger"
              size="mini"
              icon="el-icon-delete"
              :disabled="selectedConversations.length === 0"
              @click="batchDeleteHistoryConversations"
            >
              批量删除
            </el-button>
            <el-button
              type="primary"
              size="mini"
              icon="el-icon-refresh"
              :loading="historyLoading"
              @click="loadHistoryPage(1)"
            >
              刷新
            </el-button>
          </div>
          <el-table
            v-loading="historyLoading"
            :data="conversations"
            style="width: 100%"
            @selection-change="handleSelectionChange"
          >
            <el-table-column
              type="selection"
              width="55"
            />
            <el-table-column prop="title" label="会话标题">
              <template slot-scope="scope">
                <div class="history-title-container">
                  <el-input
                    v-if="scope.row.isEditing"
                    ref="titleInput"
                    v-model="scope.row.editingTitle"
                    size="small"
                    placeholder="输入标题"
                    :maxlength="100"
                    :show-word-limit="true"
                    @blur="finishTitleEdit(scope.row)"
                    @keyup.enter.native="finishTitleEdit(scope.row)"
                  >
                    <el-button
                      slot="append"
                      icon="el-icon-check"
                      @click="finishTitleEdit(scope.row)"
                    />
                  </el-input>
                  <div
                    v-else
                    class="history-title"
                    @click="startTitleEdit(scope.row)"
                  >
                    {{ scope.row.title }}
                    <i class="el-icon-edit edit-icon" />
                  </div>
                  <div class="history-time">
                    {{ formatDateTime(scope.row.createdAt) }}
                  </div>
                </div>
              </template>
            </el-table-column>
            <el-table-column align="right" width="120">
              <template slot-scope="scope">
                <el-button
                  size="mini"
                  type="primary"
                  icon="el-icon-view"
                  title="加载会话"
                  @click.stop="loadHistoryConversation(scope.row)"
                />
                <el-button
                  size="mini"
                  type="danger"
                  icon="el-icon-delete"
                  title="删除会话"
                  @click.stop="deleteHistoryConversation(scope.row)"
                />
              </template>
            </el-table-column>
          </el-table>
          <div class="history-pagination">
            <el-pagination
              :current-page="historyPagination.currentPage"
              :page-size="historyPagination.pageSize"
              layout="prev, pager, next, total"
              :total="historyPagination.total"
              background
              :disabled="historyLoading"
              @current-change="handleHistoryPageChange"
            />
          </div>
        </div>
      </div>
    </el-drawer>

    <!-- 设置对话框 -->
    <el-dialog
      title="对话设置"
      :visible.sync="settingsDialogVisible"
      width="30%"
    >
      <div class="settings-content">
        <el-form label-position="top" :model="settings" size="small">
          <el-form-item label="单个对话最大消息数量">
            <el-input-number
              v-model="settings.maxMessagesPerConversation"
              :min="10"
              :max="200"
              :step="10"
            />
            <div class="setting-tip">
              超过此数量将自动创建新对话，建议设置为30-100之间的值
            </div>
          </el-form-item>
          <el-form-item>
            <el-checkbox v-model="settings.autoCreateNewConversation">
              自动创建新对话
            </el-checkbox>
            <div class="setting-tip">
              关闭后，即使超过最大消息数量也不会自动创建新对话
            </div>
          </el-form-item>

          <el-divider content-position="left">存储管理</el-divider>
          <div class="storage-management">
            <div class="storage-info">
              <p><strong>存储使用情况：</strong></p>
              <div class="storage-stats">
                <div class="storage-item">
                  <span>AI聊天数据：</span>
                  <span class="storage-value">{{ storageStats.aiChatSizeMB }} MB</span>
                </div>
                <div class="storage-item">
                  <span>总存储：</span>
                  <span class="storage-value">{{ storageStats.totalSizeMB }} MB</span>
                </div>
                <div class="storage-item">
                  <span>使用率：</span>
                  <span class="storage-value" :style="{color: storageStats.usagePercentage > 80 ? '#f56c6c' : '#67c23a'}">
                    {{ storageStats.usagePercentage.toFixed(1) }}%
                  </span>
                </div>
              </div>
              <el-progress
                :percentage="Math.min(storageStats.usagePercentage, 100)"
                :status="storageStats.usagePercentage > 80 ? 'exception' : 'success'"
                :stroke-width="10"
                class="storage-progress"
              />
            </div>

            <div class="storage-actions">
              <el-button
                size="small"
                type="info"
                icon="el-icon-refresh"
                @click="refreshStorageStats"
              >
                刷新统计
              </el-button>
              <el-button
                size="small"
                type="warning"
                icon="el-icon-delete"
                @click="cleanOldConversationsManually"
              >
                清理旧会话
              </el-button>
              <el-button
                size="small"
                type="danger"
                icon="el-icon-delete-solid"
                @click="clearAllChatData"
              >
                清空所有数据
              </el-button>
            </div>
          </div>

          <el-divider content-position="left">性能说明</el-divider>
          <div class="performance-tips">
            <p><i class="el-icon-info" /> 消息数量与性能关系：</p>
            <ul>
              <li>消息数量<strong>30条以内</strong>：性能最佳，响应迅速</li>
              <li>消息数量<strong>30-50条</strong>：性能良好，可能有轻微延迟</li>
              <li>消息数量<strong>50-100条</strong>：性能一般，可能出现明显延迟</li>
              <li>消息数量<strong>100条以上</strong>：性能较差，可能导致页面卡顿</li>
            </ul>
            <p>建议：当对话内容较多时，创建新对话以保持最佳性能</p>
          </div>
        </el-form>
      </div>
      <span slot="footer" class="dialog-footer">
        <el-button @click="settingsDialogVisible = false">取消</el-button>
        <el-button type="primary" @click="saveSettings">保存</el-button>
      </span>
    </el-dialog>

    <!-- 添加URL对话框 -->
    <el-dialog
      title="添加URL附件"
      :visible.sync="urlDialogVisible"
      width="500px"
      :before-close="handleCloseUrlDialog"
    >
      <el-form ref="urlForm" :model="urlForm" :rules="urlRules" label-width="80px">
        <el-form-item label="URL地址" prop="url">
          <el-input
            v-model="urlForm.url"
            placeholder="请输入完整的URL地址，如: https://example.com/image.jpg"
            @keyup.enter.native="addUrlAttachment"
          />
        </el-form-item>
        <el-form-item label="显示名称" prop="displayName">
          <el-input
            v-model="urlForm.displayName"
            placeholder="可选：为该URL设置一个显示名称"
            maxlength="100"
            show-word-limit
          />
        </el-form-item>
        <div class="url-tips">
          <p><strong>支持的URL类型：</strong></p>
          <ul>
            <li>图片：.jpg, .jpeg, .png, .gif, .webp, .svg</li>
            <li>文档：.pdf, .doc, .docx, .txt</li>
            <li>其他：任何可访问的URL</li>
          </ul>
          <p class="tip-note">💡 提示：AI生成的图片可以直接复制URL添加为附件</p>
        </div>
      </el-form>

      <span slot="footer" class="dialog-footer">
        <el-button @click="urlDialogVisible = false">取消</el-button>
        <el-button type="primary" :loading="urlAddingLoading" @click="addUrlAttachment">添加</el-button>
      </span>
    </el-dialog>

    <!-- 图片预览对话框 -->
    <el-dialog
      title="图片预览"
      :visible.sync="imagePreviewVisible"
      width="80%"
      top="20px"
      append-to-body
    >
      <div class="image-preview-container">
        <img
          :src="currentPreviewImage"
          alt="预览图片"
          style="width: 100%; height: auto; max-height: 70vh; object-fit: contain; cursor: pointer;"
          @click="imagePreviewVisible = false"
        >
      </div>
      <span slot="footer" class="dialog-footer">
        <el-button type="success" icon="el-icon-paperclip" @click="addImageAsAttachment">作为附件</el-button>
        <el-button type="primary" icon="el-icon-copy-document" @click="copyImageUrl">复制图片链接</el-button>
        <el-button @click="imagePreviewVisible = false">关闭</el-button>
      </span>
    </el-dialog>

    <!-- 参考素材选择弹窗 -->
    <el-dialog
      title="选择参考素材"
      :visible.sync="referenceDialogVisible"
      width="80%"
      top="5%"
      append-to-body
    >
      <div class="reference-dialog-content">
        <div class="dialog-header">
          <div class="selection-info">
            <span>共 {{ nodeReferences.length }} 个素材{{ selectedReferences.length > 0 ? '，已选择' : '，未选择' }}</span>
            <div v-if="selectedReferences.length > 0" class="selection-actions">
              <el-button size="mini" @click="clearSelectedReferences">取消选择</el-button>
            </div>
          </div>
        </div>

        <!-- 全部参考素材网格 -->
        <div class="dialog-references-grid">
          <div
            v-for="reference in nodeReferences"
            :key="reference.id"
            class="dialog-reference-item"
            :class="{ 'selected': isReferenceSelected(reference) }"
            :title="reference.filename || '未命名文件'"
            @click="toggleReferenceSelection(reference)"
            @mouseenter="handleReferenceHover(reference, $event)"
            @mouseleave="hideImagePreview"
          >
            <!-- 文件预览 -->
            <div class="file-preview">
              <div v-if="isImageFile(reference.filename)" class="image-thumbnail">
                <img :src="reference.file_url" :alt="reference.filename" @error="handleImageError">
              </div>
              <div v-else class="file-icon-large">
                <i v-if="isExcelFile(reference.filename)" class="el-icon-document" style="color: #27ae60;" />
                <i v-else-if="isPDFFile(reference.filename)" class="el-icon-document" style="color: #e74c3c;" />
                <i v-else class="el-icon-document" style="color: #606266;" />
              </div>
            </div>
          </div>
        </div>
      </div>

      <span slot="footer" class="dialog-footer">
        <el-button @click="referenceDialogVisible = false">取消</el-button>
        <el-button type="primary" @click="confirmReferenceSelection">
          确定
        </el-button>
      </span>
    </el-dialog>

    <!-- 图片hover预览 -->
    <div
      v-if="imagePreview.visible"
      class="image-hover-preview"
      :style="{ left: imagePreview.x + 'px', top: imagePreview.y + 'px' }"
    >
      <img :src="imagePreview.src" :alt="imagePreview.alt">
    </div>
  </div>
</template>

<script>
import { GetDt } from '@/api/cms/aimodeltree'
import { getToken } from '@/utils/auth'
import { Translate } from '@/api/crm/crmmanage'
import { getReferences } from '@/api/cms/references'
import { optimizePrompt } from '@/api/ai_chat'
import MarkdownIt from 'markdown-it'
import AIChatInput from '@/components/AIChatInput/index.vue'

export default {
  name: 'Aisopchat',
  components: {
    AIChatInput
  },
  props: {
    sop_name: {
      type: String,
      default: ''
    }
  },
  data() {
    return {
      pageLoading: false,
      treeData: [],
      filterText: '',
      chatMessages: [],
      inputMessage: '',
      continuousInputMessage: '', // 连续对话输入框内容
      placeholder: '输入产品或者服务需求',
      loading: false,
      sendingMessage: false, // 发送按钮loading状态
      sendingTimeoutId: null, // 发送按钮超时计时器
      currentModel: null,
      checkedKeys: [],
      treeCollapsed: false, // 控制树形选择器的折叠状态，默认展开
      controller: null, // 用于取消请求
      timeoutId: null, // 用于超时处理
      currentConversationId: null,
      conversations: [],
      historyDrawerVisible: false,
      copySuccess: false, // 复制成功标志
      translating: false, // 翻译状态标志
      historyPagination: {
        currentPage: 1,
        pageSize: 20,
        total: 0
      },
      historyLoading: false,
      historySearchText: '',
      selectedConversations: [],
      maxMessagesPerConversation: 50, // 单个对话最大消息数量
      autoCreateNewConversation: true, // 是否自动创建新对话
      settingsDialogVisible: false, // 设置对话框可见性
      settings: {
        maxMessagesPerConversation: 50,
        autoCreateNewConversation: true
      },
      isTimeout: false, // 添加超时标识
      receivedFirstChunk: false, // 添加到data中
      upload_url: process.env.VUE_APP_BASE_API + '/cms/api/aichat/',
      listObj: {}, // 添加文件对象列表
      uploadedFiles: [], // 添加已上传文件列表
      userScrolled: false, // 用户是否主动滚动
      autoScrollEnabled: true, // 是否启用自动滚动

      // AI默认参考素材相关
      nodeReferences: [], // 当前节点的默认参考素材
      selectedReferences: [], // 用户选中的默认参考素材

      // 图片预览相关
      imagePreview: {
        visible: false,
        src: '',
        alt: '',
        x: 0,
        y: 0
      },

      // URL附件相关
      urlDialogVisible: false,
      urlAddingLoading: false,
      // 参考素材对话框相关
      referenceDialogVisible: false,
      // 图片预览相关
      imagePreviewVisible: false,
      currentPreviewImage: '',
      urlForm: {
        url: '',
        displayName: ''
      },
      urlRules: {
        url: [
          { required: true, message: '请输入URL地址', trigger: 'blur' },
          { type: 'url', message: '请输入有效的URL地址', trigger: 'blur' }
        ]
      },
      lastScrollTop: 0, // 上次滚动位置
      scrollThreshold: 30, // 接近底部的阈值
      storageStats: {
        aiChatSizeMB: 0,
        totalSizeMB: 0,
        usagePercentage: 0
      },
      // 图片生成参数
      imageParams: {
        aspectRatio: '', // 图片宽高比
        resolution: '' // 图片分辨率
      },
      // 优化提示词状态
      optimizingPrompt: false
    }
  },
  computed: {
    // 获取请求头
    headers() {
      return {
        'Authorization': getToken(),
        'PTOKEN': this.$store.getters.projectId
      }
    },

    // 文件上传请求头
    uploadHeaders() {
      return {
        'Authorization': getToken(),
        'PTOKEN': this.$store.getters.projectId
      }
    },

    // 存储键前缀
    storageKeyPrefix() {
      return 'aisop_chat_'
    },
    // 上传数据
    uploadData() {
      return {
        model_id: this.currentModel ? this.currentModel.id : '',
        project_id: this.$store.getters.projectId,
        user_message: this.inputMessage,
        type: 'file_upload'
      }
    },
    // 显示的参考素材（限制为12个）
    displayedReferences() {
      return this.nodeReferences.slice(0, 12)
    }
  },
  watch: {
    filterText(val) {
      this.$refs.treeRef.filter(val)
    }
  },
  created() {
    // 初始化对话列表
    this.initConversations()
    // 加载设置
    this.loadSettings()
    // 初始化存储统计
    this.refreshStorageStats()
  },
  mounted() {
    this.getTreeData()

    // 检查URL参数中是否有节点ID，如果有则自动选择该节点
    this.checkAndSelectNodeFromUrl()

    // 添加全局函数用于iframe相关操作，使用箭头函数保持this的指向
    window.resizeIframe = (iframe) => this.resizeIframe(iframe)
    window.toggleIframeSize = (id) => this.toggleIframeSize(id)
    window.copyHtmlContent = (id) => this.copyHtmlContent(id)
    // 为全局访问设置组件实例，用于markdown图片点击预览
    window.vueComponentInstance = this

    // 添加滚动事件监听
    if (this.$refs.chatContainerRef) {
      this.$refs.chatContainerRef.addEventListener('scroll', this.handleScroll)
    }

    // 为markdown图片添加点击事件监听
    this.$nextTick(() => {
      this.addMarkdownImageClickListeners()
    })
  },
  beforeDestroy() {
    // 取消任何正在进行的请求
    if (this.controller) {
      try {
        // 显式设置中止原因，防止未捕获的异常
        this.controller.abort('component_destroyed')
      } catch (error) {
        console.warn('中止请求时发生错误:', error)
      }
      this.controller = null
    }

    // 清除超时计时器
    if (this.timeoutId) {
      try {
        clearTimeout(this.timeoutId)
      } catch (error) {
        console.warn('清除计时器时发生错误:', error)
      }
      this.timeoutId = null
    }

    // 移除滚动事件监听
    if (this.$refs.chatContainerRef) {
      this.$refs.chatContainerRef.removeEventListener('scroll', this.handleScroll)
    }

    // 清理全局函数
    window.resizeIframe = null
    window.toggleIframeSize = null
    window.copyHtmlContent = null
    // 清理全局组件引用
    if (window.vueComponentInstance === this) {
      window.vueComponentInstance = null
    }
  },
  updated() {
    // 组件更新后检查滚动容器并添加事件监听（防止refs延迟加载）
    if (this.$refs.chatContainerRef && !this._scrollListenerAdded) {
      this.$refs.chatContainerRef.addEventListener('scroll', this.handleScroll)
      this._scrollListenerAdded = true
    }
  },
  methods: {
    // iframe相关函数
    resizeIframe(iframe) {
      try {
        iframe.style.height = iframe.contentWindow.document.body.scrollHeight + 20 + 'px'
      } catch (e) {
        console.error('无法调整iframe高度:', e)
      }
    },

    toggleIframeSize(id) {
      const iframe = document.getElementById(id)
      if (!iframe) return

      try {
        // 获取或创建遮罩层的ID
        const overlayId = `overlay-${id}`

        // 标记当前状态
        if (iframe.classList.contains('fullscreen')) {
          // 恢复正常大小
          iframe.classList.remove('fullscreen')
          iframe.style.position = ''
          iframe.style.top = ''
          iframe.style.left = ''
          iframe.style.width = '100%'
          iframe.style.height = 'auto'
          iframe.style.zIndex = ''
          iframe.style.backgroundColor = ''

          // 删除遮罩层
          const overlay = document.getElementById(overlayId)
          if (overlay) {
            document.body.removeChild(overlay)
          }

          // 删除提示信息
          const tipId = `tip-${id}`
          const tipElement = document.getElementById(tipId)
          if (tipElement) {
            document.body.removeChild(tipElement)
          }

          // 重新调整为内容高度
          this.resizeIframe(iframe)

          // 防止滚动条问题
          document.body.style.overflow = ''
          console.log('已恢复正常大小')
        } else {
          // 创建遮罩层
          let overlay = document.getElementById(overlayId)
          if (!overlay) {
            overlay = document.createElement('div')
            overlay.id = overlayId
            overlay.className = 'iframe-overlay'

            // 添加关闭按钮
            const closeBtn = document.createElement('button')
            closeBtn.innerHTML = '<i class="el-icon-close"></i> 点击此处关闭全屏'
            closeBtn.className = 'iframe-close-btn'
            closeBtn.addEventListener('click', (e) => {
              e.stopPropagation() // 阻止事件冒泡
              this.toggleIframeSize(id)
            })

            // 将iframe克隆并添加到遮罩层
            const originalIframe = iframe
            const clonedIframe = document.createElement('iframe')

            // 复制原始iframe的属性
            clonedIframe.id = `${id}-fullscreen`
            clonedIframe.className = 'html-iframe fullscreen'
            clonedIframe.srcdoc = originalIframe.getAttribute('srcdoc')
            clonedIframe.frameBorder = '0'
            clonedIframe.width = '80%'
            clonedIframe.style.height = '80vh'
            clonedIframe.style.backgroundColor = 'white'
            clonedIframe.style.borderRadius = '8px'
            clonedIframe.style.boxShadow = '0 0 20px rgba(0, 0, 0, 0.5)'
            clonedIframe.style.zIndex = '99999'

            // 将克隆的iframe添加到遮罩层
            overlay.appendChild(clonedIframe)
            overlay.appendChild(closeBtn)

            // 点击遮罩层也可以关闭
            overlay.addEventListener('click', () => {
              this.toggleIframeSize(id)
            })

            document.body.appendChild(overlay)

            // 添加提示信息
            const tipElement = document.createElement('div')
            tipElement.id = `tip-${id}`
            tipElement.className = 'iframe-fullscreen-tip'
            tipElement.innerHTML = '您可以点击右上角红色按钮或任意空白区域关闭全屏'
            document.body.appendChild(tipElement)

            // 调整克隆的iframe高度
            setTimeout(() => {
              try {
                clonedIframe.style.height = clonedIframe.contentWindow.document.body.scrollHeight + 20 + 'px'
              } catch (e) {
                console.error('无法调整iframe高度:', e)
              }
            }, 300)
          }

          // 防止滚动条问题
          document.body.style.overflow = 'hidden'

          // 标记原始iframe为全屏模式
          iframe.classList.add('fullscreen')

          console.log('已切换为全屏模式')
        }
      } catch (e) {
        console.error('切换全屏状态失败:', e)
      }
    },

    copyHtmlContent(id) {
      // 尝试获取原始iframe或全屏iframe
      let iframe = document.getElementById(id)
      if (!iframe) {
        // 如果找不到原始iframe，尝试查找全屏版本
        iframe = document.getElementById(`${id}-fullscreen`)
        if (!iframe) return
      }

      try {
        // 获取iframe中的HTML内容
        const htmlContent = iframe.getAttribute('srcdoc')

        // 确保复制的是纯HTML内容，没有转义
        const decodedHtml = htmlContent
          .replace(/&quot;/g, '"')
          .replace(/&#39;/g, "'")

        navigator.clipboard.writeText(decodedHtml).then(() => {
          this.$message.success('HTML代码已复制到剪贴板')
        }).catch(err => {
          console.error('复制失败:', err)
          this.$message.error('复制失败，请手动复制')
        })
      } catch (e) {
        console.error('复制HTML内容失败:', e)
        this.$message.error('复制失败，请手动复制')
      }
    },

    // 复制消息内容 - 更新调用的方法名
    async copyMessageContent(content, isAssistant = false) {
      try {
        if (isAssistant) {
          // 对于AI消息，复制渲染后的HTML
          const renderedHtml = this.renderMarkdown(content)
          const plainText = content // 保留原始Markdown作为纯文本备用

          try {
            // 使用重命名后的方法
            await this.copyFormattedContent(renderedHtml, plainText)
            this.$message.success('富文本内容已复制到剪贴板')
          } catch (err) {
            console.error('复制富文本失败:', err)
            // 如果富文本复制失败，尝试复制纯文本
            if (navigator.clipboard && navigator.clipboard.writeText) {
              await navigator.clipboard.writeText(plainText)
            } else {
              const textArea = document.createElement('textarea')
              textArea.value = plainText
              document.body.appendChild(textArea)
              textArea.select()
              document.execCommand('copy')
              document.body.removeChild(textArea)
            }
            this.$message.success('内容已复制到剪贴板（纯文本模式）')
          }
        } else {
          // 对于用户消息，仍然复制纯文本
          if (navigator.clipboard && navigator.clipboard.writeText) {
            await navigator.clipboard.writeText(content)
          } else {
            const textArea = document.createElement('textarea')
            textArea.value = content
            document.body.appendChild(textArea)
            textArea.select()
            document.execCommand('copy')
            document.body.removeChild(textArea)
          }
          this.$message.success('内容已复制到剪贴板')
        }
      } catch (err) {
        console.error('复制操作失败:', err)
        this.$message.error('复制失败，请手动复制')
      }
    },

    // 获取树形数据
    async getTreeData() {
      this.pageLoading = true
      try {
        // 添加时间戳参数，确保不使用缓存
        const timestamp = new Date().getTime()
        const response = await GetDt({
          'sop_name': this.sop_name,
          '_t': timestamp // 添加时间戳参数
        })
        this.treeData = response.data
        console.log('树形数据结构:', this.treeData)
        this.pageLoading = false

        // 数据加载完成后，尝试加载保存的会话
        this.$nextTick(() => {
          if (this.currentConversationId) {
            this.loadConversation(this.currentConversationId)
          }
        })
      } catch (error) {
        this.$message.error('获取模型数据失败')
        console.error(error)
        this.pageLoading = false
      }
    },

    // 过滤树节点
    filterNode(value, data) {
      if (!value) return true
      return data.name.toLowerCase().includes(value.toLowerCase())
    },

    // 选择模型
    // 切换树形选择器的折叠状态
    toggleTreeCollapse() {
      this.treeCollapsed = !this.treeCollapsed
    },

    handleNodeClick(data) {
      // 如果节点是禁用的，不允许选择
      if (data.disabled) {
        // this.$message.warning('该节点不可选择')
        return
      }

      // 只有叶子节点才能选择
      if (!data.children || data.children.length === 0) {
        // 先清空之前选中的所有节点
        this.checkedKeys.forEach(key => {
          const node = this.findNodeByKey(key)
          if (node) {
            this.$refs.treeRef.setChecked(node, false)
          }
        })

        // 设置选中状态
        this.$refs.treeRef.setChecked(data, true)
        // 设置当前高亮节点 - 关键！用于CSS样式 .el-tree-node.is-current
        this.$refs.treeRef.setCurrentKey(this.getNodeKey(data))

        // 更新当前模型
        this.currentModel = data

        // 如果有placeholder，在右侧对话框中显示
        if (data.placeholder) {
          this.placeholder = data.placeholder
          this.inputMessage = data.placeholder
        }

        // 更新选中的节点
        const nodeKey = this.getNodeKey(data)
        this.checkedKeys = [nodeKey]

        // 加载节点的默认参考素材
        this.loadNodeReferences(data.id)

        // 🆕 开启新对话：清空聊天历史，创建新会话
        this.createNewConversation()

        // this.$message.success(`已选择模型: ${data.name}`)
      }
    },

    // 格式化时间
    formatTime() {
      const now = new Date()
      return `${now.getHours().toString().padStart(2, '0')}:${now.getMinutes().toString().padStart(2, '0')}`
    },

    // 发送消息
    // 优化提示词
    async optimizePrompt() {
      if (!this.inputMessage.trim()) {
        this.$message.warning('请先输入提示词')
        return
      }

      if (!this.currentModel) {
        this.$message.warning('请先选择一个AI模型')
        return
      }

      this.optimizingPrompt = true

      try {
        const response = await optimizePrompt({
          prompt: this.inputMessage,
          ai_type: this.currentModel.ai_type || 'text'
        })

        if (response.success) {
          // 更新输入框内容为优化后的提示词
          this.inputMessage = response.data.optimized_prompt
          this.$message.success('提示词优化成功')
        } else {
          this.$message.error(response.msg || '优化失败')
        }
      } catch (error) {
        console.error('优化提示词失败:', error)
        this.$message.error('优化提示词失败: ' + (error.message || '未知错误'))
      } finally {
        this.optimizingPrompt = false
      }
    },

    async sendMessage() {
      // 从AI聊天输入框获取内容（纯文本）
      const editorContent = this.$refs.chatInput ? this.$refs.chatInput.getContent() : this.inputMessage

      // 检查消息内容、上传文件列表和选中的默认参考素材是否都为空
      if (!editorContent.trim() && this.uploadedFiles.length === 0 && this.selectedReferences.length === 0) {
        // 如果都为空，不发送消息
        return
      }
      if (!this.currentModel) {
        this.$message.warning('请先选择一个AI模型')
        return
      }

      // 设置发送按钮为loading状态
      this.sendingMessage = true

      // 清除之前的超时计时器
      if (this.sendingTimeoutId) {
        clearTimeout(this.sendingTimeoutId)
      }

      // 设置10秒超时，自动恢复发送按钮状态
      this.sendingTimeoutId = setTimeout(() => {
        this.sendingMessage = false
        console.log('发送按钮已超时自动恢复')
      }, 10000)

      // 检查存储空间
      try {
        const storageInfo = this.getStorageUsage()
        if (storageInfo.usagePercentage > 95) {
          this.$message.warning('存储空间不足，请清理旧会话后再继续')
          return
        }
      } catch (error) {
        console.warn('检查存储空间失败:', error)
      }

      // 如果没有当前会话，创建一个新会话
      if (!this.currentConversationId) {
        this.createNewConversation()
      }

      // 检查当前对话消息数量是否超过限制
      if (this.autoCreateNewConversation && this.chatMessages.length >= this.maxMessagesPerConversation) {
        // 保存当前会话
        this.saveCurrentConversation()

        // 提示用户
        this.$message.info(`当前对话已达到 ${this.maxMessagesPerConversation} 条消息上限，已自动创建新对话`)

        // 创建新会话
        this.createNewConversation()
      } else if (this.chatMessages.length >= this.maxMessagesPerConversation * 0.9 && this.chatMessages.length < this.maxMessagesPerConversation) {
        // 当消息数量接近上限但未达到上限时，显示警告
        this.$message.warning(`当前对话消息数量接近上限(${this.chatMessages.length}/${this.maxMessagesPerConversation})，建议创建新对话`)
      }

      // 如果有正在进行的请求，先取消
      if (this.controller) {
        this.controller.abort()
        this.controller = null
      }

      // 合并上传的文件和选中的默认参考素材
      const allFiles = [
        ...this.uploadedFiles,
        ...this.selectedReferences.map(ref => ({
          uid: ref.id,
          file_name: ref.filename,
          file_url: ref.file_url,
          original_name: ref.filename,
          display_name: ref.filename,
          file_type: this.getFileTypeFromReference(ref),
          isReference: true // 标记为默认参考素材
        }))
      ]

      // 添加用户消息
      this.chatMessages.push({
        role: 'user',
        content: editorContent,
        time: this.formatTime(),
        files: allFiles // 包含上传文件和选中的默认参考素材
      })

      // 构建发送给AI的消息内容
      let userMessage = editorContent

      // 如果选择了参考素材，在消息后面添加参考素材链接提示
      if (this.selectedReferences.length > 0) {
        const referenceUrls = this.selectedReferences.map(ref => ref.file_url).join(',')
        const referenceHint = `##参考素材链接:${referenceUrls}`

        // 如果用户有输入内容，则换行添加；否则直接设置为提示内容
        if (userMessage.trim()) {
          userMessage = `${userMessage}\n${referenceHint}`
        } else {
          userMessage = referenceHint
        }
      }

      const files = allFiles // 保存合并后的文件列表

      // 清空AI聊天输入框内容
      if (this.$refs.chatInput) {
        this.$refs.chatInput.clearContent()
      } else {
        this.inputMessage = ''
      }
      // 发送后不自动清空附件列表，用户可手动清空

      // 滚动到底部
      await this.scrollToBottom()

      // 显示加载状态
      this.loading = true

      // 等待DOM更新后滚动到底部显示思考提示
      // 使用setTimeout确保思考提示完全渲染后再滚动
      await this.$nextTick()
      setTimeout(() => {
        this.scrollToBottom()
      }, 100)

      // 添加AI回复占位消息
      const assistantMessageIndex = this.chatMessages.length
      this.chatMessages.push({
        role: 'assistant',
        content: '',
        time: this.formatTime()
      })

      // 设置重试参数 - 在函数内部定义变量
      const maxRetries = 3 // 最大重试次数
      let retryCount = 0
      const retryDelay = 2000 // 重试间隔时间(毫秒)

      // 尝试发送请求的函数
      const tryRequest = async() => {
        try {
        // 创建请求对象
          this.controller = new AbortController()
          const signal = this.controller.signal

          // 清除上一个可能存在的超时计时器
          if (this.timeoutId) {
            clearTimeout(this.timeoutId)
          }

          // 重置接收标志
          this.receivedFirstChunk = false

          // 设置初始响应超时
          this.timeoutId = setTimeout(() => {
            if (this.controller && !this.receivedFirstChunk) {
            // 仅在尚未收到第一个数据块时才执行超时中断
              this.isTimeout = true
              this.controller.abort('initial_timeout')
            }
          }, 60000) // 60秒

          // 构建聊天历史记录（排除当前用户消息和刚创建的助手消息）
          // 限制最近10轮对话（20条消息：10条用户+10条助手）
          const maxHistoryMessages = 20
          const historyMessages = this.chatMessages
            .slice(0, -2) // 排除最后两条（当前用户消息和刚创建的助手消息）
            .slice(-maxHistoryMessages) // 只取最近的N条
            .map(msg => ({
              role: msg.role === 'user' ? 'user' : 'assistant',
              content: msg.content
            }))

          // 准备请求参数
          const requestData = {
            model_id: this.currentModel.id,
            user_message: userMessage,
            stream: true,
            files: files.map(file => file.file_url), // 添加文件URL列表
            chat_history: historyMessages, // 添加聊天历史
            // 添加图片生成参数（如果当前模型是图片类型）
            aspect_ratio: this.imageParams.aspectRatio || '',
            resolution: this.imageParams.resolution || ''
          }

          // 获取API基础URL
          const baseUrl = process.env.VUE_APP_BASE_API || ''

          // 发起流式请求
          const response = await fetch(`${baseUrl}/cms/api/aichat/`, {
            method: 'post',
            headers: this.headers,
            body: JSON.stringify(requestData),
            signal: signal,
            mode: 'cors'
          })

          if (!response.ok) {
            throw new Error(`HTTP error! status: ${response.status}`)
          }

          // 获取响应的可读流
          const reader = response.body.getReader()
          const decoder = new TextDecoder('utf-8')
          let responseText = ''
          let bufferText = ''

          // 流处理变量
          let streamingTimeoutId = null
          let lastUpdateTime = Date.now()

          // 重置流超时函数
          const resetStreamingTimeout = () => {
            if (streamingTimeoutId) {
              clearTimeout(streamingTimeoutId)
            }

            streamingTimeoutId = setTimeout(() => {
              if (this.controller) {
                console.warn('流数据传输超时')

                // 如果有重试机会，给用户提示
                if (retryCount < maxRetries) {
                  const currentContent = this.chatMessages[assistantMessageIndex].content
                  if (!currentContent.includes('[连接中断，正在尝试恢复...]')) {
                    this.safeUpdateContent(
                      assistantMessageIndex,
                      currentContent + '\n\n[连接中断，正在尝试恢复...]'
                    )
                  }

                  // 中断当前请求
                  this.controller.abort('streaming_timeout')
                } else {
                  // 无重试机会，直接提示超时
                  this.isTimeout = true
                  this.controller.abort('streaming_timeout')
                }
              }
            }, 60000) // 15秒超时
          }

          // 是否应该更新UI的判断函数
          const shouldUpdateUI = () => {
            const now = Date.now()
            if (now - lastUpdateTime > 300) {
              lastUpdateTime = now
              return true
            }
            return false
          }

          // 初始化流超时
          resetStreamingTimeout()

          // 处理流数据
          let consecutiveErrorCount = 0

          // eslint-disable-next-line no-constant-condition
          while (true) {
            try {
              const { done, value } = await reader.read()

              if (done) {
                // 处理剩余的缓冲区内容
                if (bufferText) {
                  this.safeUpdateContent(assistantMessageIndex, responseText + bufferText)
                }
                break
              }

              // 更新接收时间
              consecutiveErrorCount = 0

              // 标记已接收第一个数据块
              if (!this.receivedFirstChunk) {
                this.receivedFirstChunk = true

                // 清除初始响应超时
                if (this.timeoutId) {
                  clearTimeout(this.timeoutId)
                  this.timeoutId = null
                }

                // 如果之前显示了恢复连接的消息，移除它
                const currentContent = this.chatMessages[assistantMessageIndex].content
                if (currentContent.includes('[连接中断，正在尝试恢复...]')) {
                  this.safeUpdateContent(
                    assistantMessageIndex,
                    currentContent.replace('[连接中断，正在尝试恢复...]', '')
                  )
                }
              }

              // 重置流超时
              resetStreamingTimeout()

              // 解码数据
              const chunk = decoder.decode(value, { stream: true })

              // 预处理特殊字符，如##等
              const safeChunk = this.preprocessMarkdown
                ? this.preprocessMarkdown(chunk)
                : chunk

              // 添加到缓冲区
              bufferText += safeChunk

              // 更新UI
              if (shouldUpdateUI()) {
                this.safeUpdateContent(assistantMessageIndex, responseText + bufferText)
                responseText += bufferText
                bufferText = ''
                this.scrollToBottom(false)
              }
            } catch (error) {
              console.error('读取流数据时出错:', error)

              if (error.name === 'AbortError') {
                // 请求被中止
                break
              }

              // 记录连续错误
              consecutiveErrorCount++

              if (consecutiveErrorCount >= 3) {
                // 连续多次错误，尝试重试
                if (retryCount < maxRetries) {
                  console.log(`连续出错${consecutiveErrorCount}次，准备重试...`)
                  break // 退出当前循环，准备重试
                } else {
                  // 达到最大重试次数
                  bufferText += '\n\n[网络连接中断，已显示部分内容]'
                  this.safeUpdateContent(assistantMessageIndex, responseText + bufferText)
                  throw new Error('max_retries_exceeded')
                }
              } else {
                // 少量错误，继续尝试
                await new Promise(resolve => setTimeout(resolve, 500))
              }
            }
          }

          // 清理计时器
          if (streamingTimeoutId) {
            clearTimeout(streamingTimeoutId)
          }

          return true
        } catch (error) {
          // 如果是超时或用户取消，直接抛出
          if (error.name === 'AbortError' ||
              error.message === 'max_retries_exceeded') {
            throw error
          }

          // 检查是否可以重试
          if (retryCount < maxRetries) {
            console.log(`请求失败，${retryDelay / 1000}秒后第${retryCount + 1}次重试...`)

            // 显示重试消息
            this.safeUpdateContent(
              assistantMessageIndex,
              (this.chatMessages[assistantMessageIndex].content || '') +
              `\n\n[连接失败，${retryDelay / 1000}秒后重试...]`
            )

            // 等待后重试
            await new Promise(resolve => setTimeout(resolve, retryDelay))
            retryCount++
            return await tryRequest()
          }

          // 达到最大重试次数
          throw error
        }
      }

      try {
        // 开始尝试请求
        await tryRequest()

        this.loading = false
        this.sendingMessage = false
        // 清除发送按钮超时计时器
        if (this.sendingTimeoutId) {
          clearTimeout(this.sendingTimeoutId)
          this.sendingTimeoutId = null
        }
        this.controller = null

        // 保存当前会话
        this.saveCurrentConversation()
      } catch (error) {
        // 使用统一的错误处理函数
        this.handleFetchError(error, assistantMessageIndex)

        // 清除计时器
        if (this.timeoutId) {
          clearTimeout(this.timeoutId)
          this.timeoutId = null
        }

        this.loading = false
        this.sendingMessage = false
        // 清除发送按钮超时计时器
        if (this.sendingTimeoutId) {
          clearTimeout(this.sendingTimeoutId)
          this.sendingTimeoutId = null
        }
        this.controller = null
        this.scrollToBottom()

        // 保存会话
        this.saveCurrentConversation()
      }
    },

    // 🆕 发送连续对话消息（保持历史上下文，直接与AI对话，不经过模板解析）
    async sendContinuousMessage() {
      // 从连续对话输入框获取内容
      const editorContent = this.$refs.continuousChatInput ? this.$refs.continuousChatInput.getContent() : this.continuousInputMessage

      // 检查消息内容是否为空
      if (!editorContent.trim()) {
        return
      }
      if (!this.currentModel) {
        this.$message.warning('请先选择一个AI模型')
        return
      }

      // 设置发送按钮为loading状态
      this.sendingMessage = true

      // 如果没有当前会话，创建一个新会话
      if (!this.currentConversationId) {
        this.createNewConversation()
      }

      // 添加用户消息到聊天记录
      this.chatMessages.push({
        role: 'user',
        content: editorContent,
        time: this.formatTime(),
        files: []
      })

      // 清空连续对话输入框
      this.continuousInputMessage = ''
      if (this.$refs.continuousChatInput) {
        this.$refs.continuousChatInput.clearContent()
      }

      // 滚动到底部
      await this.scrollToBottom()

      // 显示加载状态
      this.loading = true

      // 添加AI回复占位消息
      const assistantMessageIndex = this.chatMessages.length
      this.chatMessages.push({
        role: 'assistant',
        content: '',
        time: this.formatTime()
      })

      try {
        // 创建请求对象
        this.controller = new AbortController()
        const signal = this.controller.signal

        // 构建聊天历史记录（排除当前用户消息和刚创建的助手消息）
        const historyMessages = this.chatMessages
          .slice(0, -2) // 排除最后两条
          .slice(-20) // 只取最近的20条
          .map(msg => ({
            role: msg.role === 'user' ? 'user' : 'assistant',
            content: msg.content
          }))

        // 准备请求参数 - 直接调用 AI 聊天接口
        const requestData = {
          model_id: this.currentModel.id,
          user_message: editorContent,
          stream: true,
          files: [],
          chat_history: historyMessages,
          // 添加图片生成参数（连续对话也需要传递）
          aspect_ratio: this.imageParams.aspectRatio || '',
          resolution: this.imageParams.resolution || ''
        }

        // 获取API基础URL
        const baseUrl = process.env.VUE_APP_BASE_API || ''

        // 发起流式请求
        const response = await fetch(`${baseUrl}/cms/api/aichat/`, {
          method: 'post',
          headers: this.headers,
          body: JSON.stringify(requestData),
          signal: signal,
          mode: 'cors'
        })

        if (!response.ok) {
          throw new Error(`HTTP error! status: ${response.status}`)
        }

        // 获取响应的可读流
        const reader = response.body.getReader()
        const decoder = new TextDecoder('utf-8')
        let responseText = ''

        // 读取流数据
        // eslint-disable-next-line no-constant-condition
        while (true) {
          const { done, value } = await reader.read()
          if (done) break

          const chunk = decoder.decode(value, { stream: true })
          responseText += chunk

          // 更新AI消息内容
          this.safeUpdateContent(assistantMessageIndex, responseText)
          await this.scrollToBottom()
        }

        // 保存会话
        this.saveCurrentConversation()
      } catch (error) {
        console.error('连续对话发送失败:', error)
        if (error.name !== 'AbortError') {
          this.$message.error('发送失败: ' + error.message)
          // 移除失败的消息
          this.chatMessages.splice(assistantMessageIndex, 1)
        }
      } finally {
        this.loading = false
        this.sendingMessage = false
        this.controller = null
      }
    },

    // 🆕 处理连续对话输入框的图片上传
    handleContinuousImageUploaded(file) {
      // 复用现有的图片上传处理逻辑
      this.handleImageUploaded(file)
    },

    // 取消当前请求
    cancelRequest() {
      if (this.controller) {
        // 设置一个明确的取消原因
        this.isTimeout = false // 确保不是超时
        this.controller.abort('user_cancel')
        this.controller = null

        // 添加终止提示
        const assistantMessage = this.chatMessages[this.chatMessages.length - 1]
        if (assistantMessage && assistantMessage.role === 'assistant') {
          assistantMessage.content += '\n\n[用户已终止回复]'
        }
      }

      // 清除超时计时器
      if (this.timeoutId) {
        clearTimeout(this.timeoutId)
        this.timeoutId = null
      }

      this.loading = false
    },

    // 滚动到底部 - 修改控制自动滚动的逻辑
    async scrollToBottom(waitForNextTick = true) {
      // 如果用户手动滚动且未滚动到底部，则不自动滚动
      if (!this.autoScrollEnabled && this.userScrolled) {
        return
      }

      if (waitForNextTick) {
        await this.$nextTick()
      }
      if (this.$refs.chatContainerRef) {
        this.$refs.chatContainerRef.scrollTop = this.$refs.chatContainerRef.scrollHeight
      }
    },

    // 处理用户滚动事件
    handleScroll() {
      const container = this.$refs.chatContainerRef
      if (!container) return

      // 计算是否在底部附近
      const isAtBottom = container.scrollHeight - container.scrollTop - container.clientHeight <= this.scrollThreshold

      // 确定滚动方向 (true = 向上, false = 向下)
      const isScrollingUp = container.scrollTop < this.lastScrollTop
      this.lastScrollTop = container.scrollTop

      // 用户向上滚动时，标记用户已滚动并禁用自动滚动
      if (isScrollingUp) {
        this.userScrolled = true
        this.autoScrollEnabled = false
      }

      // 用户滚动到底部时，启用自动滚动
      if (isAtBottom) {
        this.userScrolled = false
        this.autoScrollEnabled = true
      }
    },

    // 增强处理头部，添加特殊字符转义防止渲染问题
    escapeSpecialChars(text) {
      return text
        .replace(/==/g, '&#61;&#61;')
        .replace(/\$/g, '&#36;')
    },

    // 处理内联Markdown元素（链接、加粗、斜体等）
    processInlineMarkdown(text) {
      // 处理链接 [text](url)
      text = text.replace(/\[([^\]]+)\]\(([^)]+)\)/g, (match, linkText, url) => {
        const safeUrl = this.sanitizeUrl(url)
        return `<a href="${safeUrl}" target="_blank" rel="noopener noreferrer" style="color: #409EFF !important; text-decoration: underline !important;">${linkText}</a>`
      })

      // 注意：不在这里处理图片，避免与MarkdownIt重复处理
      // 图片处理交给renderMarkdown方法中的MarkdownIt处理

      // 处理加粗 **text** 或 __text__
      text = text.replace(/(\*\*|__)(.*?)\1/g, '<strong>$2</strong>')

      // 处理斜体 *text* 或 _text_
      text = text.replace(/(\*|_)([^\*_]+)\1/g, '<em>$2</em>')

      // 处理删除线 ~~text~~
      text = text.replace(/~~(.*?)~~/g, '<del>$1</del>')

      // 处理行内代码 `code`
      text = text.replace(/`([^`]+)`/g, '<code>$1</code>')

      // 处理表情符号 :emoji:
      text = text.replace(/:([\w-]+):/g, '<span class="emoji">:$1:</span>')

      // 处理换行，保留单行换行为<br>
      text = text.replace(/\n/g, '<br>')

      // 处理加粗文本 **text**
      text = text.replace(/\*\*([^*]+)\*\*/g, '<strong>$1</strong>')

      // 处理斜体文本 *text*（如果需要）
      text = text.replace(/\*([^*]+)\*/g, '<em>$1</em>')

      // 处理代码 `code`（如果需要）
      text = text.replace(/`([^`]+)`/g, '<code>$1</code>')

      return text
    },

    // 保留HTML转义辅助函数
    escapeHtml(text) {
      return text
        .replace(/&/g, '&amp;')
        .replace(/</g, '&lt;')
        .replace(/>/g, '&gt;')
        .replace(/"/g, '&quot;')
        .replace(/'/g, '&#39;')
    },

    // URL安全检查
    sanitizeUrl(url) {
      // 简单的URL清理，确保链接是安全的
      url = url.trim()
      if (url.toLowerCase().startsWith('javascript:')) {
        return '#'
      }
      return url
    },

    // 处理图片路径 - 保留原有功能
    processImagePath(src) {
      // 获取API基础URL
      const baseUrl = process.env.VUE_APP_BASE_API || ''

      // 如果src是绝对路径或数据URL，直接返回
      if (src.startsWith('http') || src.startsWith('https') || src.startsWith('data:')) {
        return src
      }

      // 相对路径添加baseUrl
      return `${baseUrl}${src.startsWith('/') ? '' : '/'}${src}`
    },

    // 获取节点的唯一标识符
    getNodeKey(data) {
      // 优先使用id，如果没有id则使用name作为唯一标识符
      return data.id || data.name
    },

    // 处理节点展开
    handleNodeExpand(data, node) {
      // 在accordion模式下不需要记住展开的节点
      console.log('节点展开:', data.name)
    },

    // 处理节点折叠
    handleNodeCollapse(data, node) {
      // 在accordion模式下不需要记住展开的节点
      console.log('节点折叠:', data.name)
    },

    // 处理节点检查
    handleCheck(data, checked) {
      // 如果节点是禁用的，不允许选择
      if (data.disabled) {
        // 取消选中
        this.$nextTick(() => {
          this.$refs.treeRef.setChecked(data, false)
        })
        this.$message.warning('该节点不可选择')
        return
      }

      // 更新选中的节点
      const nodeKey = this.getNodeKey(data)

      if (checked.checked) {
        // 如果是选中操作
        // 先清空之前选中的所有节点
        this.checkedKeys.forEach(key => {
          if (key !== nodeKey) {
            const node = this.findNodeByKey(key)
            if (node) {
              this.$refs.treeRef.setChecked(node, false)
            }
          }
        })

        // 重置选中节点数组
        this.checkedKeys = [nodeKey]

        // 更新当前模型
        this.currentModel = data

        // 如果有placeholder，在右侧对话框中显示
        if (data.placeholder) {
          this.inputMessage = data.placeholder
        }
      } else {
        // 如果是取消选中操作
        this.checkedKeys = this.checkedKeys.filter(key => key !== nodeKey)

        // 如果当前模型是被取消选中的节点，则清空当前模型
        if (this.currentModel && this.getNodeKey(this.currentModel) === nodeKey) {
          this.currentModel = null
          this.inputMessage = ''
        }
      }
    },

    // 根据key查找节点
    findNodeByKey(key) {
      const findNode = (nodes) => {
        for (let i = 0; i < nodes.length; i++) {
          const node = nodes[i]
          const nodeKey = this.getNodeKey(node)

          if (nodeKey === key) {
            return node
          }

          if (node.children && node.children.length > 0) {
            const found = findNode(node.children)
            if (found) return found
          }
        }

        return null
      }

      return findNode(this.treeData)
    },

    // 自定义节点渲染
    renderContent(h, { node, data, store }) {
      // 设置节点样式：使用节点的color字段设置颜色，对禁用节点设置鼠标样式
      const style = {
        color: data.color || '', // 使用节点的color字段，如果没有则使用默认颜色
        cursor: data.disabled ? 'not-allowed' : 'pointer'
      }

      return h('span', {
        style: style
      }, [
        h('span', node.label)
      ])
    },

    // 刷新树形数据
    async refreshTreeData() {
      if (this.pageLoading) return // 如果正在加载，不重复请求

      try {
        // 直接调用getTreeData方法，它已经包含了加载状态的处理
        await this.getTreeData()
        this.$message.success('数据已刷新')
      } catch (error) {
        // getTreeData方法中已经处理了错误，这里不需要重复处理
        console.error('刷新树形数据失败', error)
      }
    },

    // 加载会话
    loadConversation(conversationId) {
      this.pageLoading = true
      const conversationJson = localStorage.getItem(`${this.storageKeyPrefix}${conversationId}`)
      if (!conversationJson) {
        // 静默处理，不显示错误提示
        this.pageLoading = false
        return
      }

      try {
        const conversation = JSON.parse(conversationJson)

        // 限制加载的消息数量，防止过多消息导致页面卡顿
        const maxMessages = 100 // 最大加载消息数
        let messages = conversation.messages || []

        if (messages.length > maxMessages) {
          // 如果消息过多，只加载最新的maxMessages条
          const truncatedCount = messages.length - maxMessages
          messages = messages.slice(-maxMessages)

          // 在消息列表开头添加提示
          messages.unshift({
            role: 'system',
            content: `由于会话消息过多，已省略较早的 ${truncatedCount} 条消息。`,
            time: this.formatTime(),
            isSystemMessage: true
          })

          this.$message.warning(`会话消息过多，已加载最新的 ${maxMessages} 条消息`)
        }

        this.chatMessages = messages

        // 恢复翻译状态
        this.chatMessages.forEach(msg => {
          if (msg.isTranslated && msg.originalContent) {
            // 确保翻译状态和原始内容被正确恢复
            msg.isTranslated = true
            msg.originalContent = msg.originalContent
          } else {
            msg.isTranslated = false
          }
          // 确保translating属性为false
          msg.translating = false
        })

        this.currentConversationId = conversationId

        // 如果会话有关联的模型，尝试选择该模型
        if (conversation.model && conversation.model.id) {
          this.$nextTick(() => {
            // 查找模型节点
            const modelNode = this.findNodeById(conversation.model.id)
            if (modelNode) {
              this.handleNodeClick(modelNode)
            } else {
              // 如果找不到对应的模型节点，至少设置一个基本的模型信息
              this.currentModel = {
                id: conversation.model.id,
                name: conversation.model.name || 'AI助手'
              }
            }
          })
        } else {
          // 如果会话没有关联模型，设置一个默认模型
          this.currentModel = {
            id: 'default',
            name: 'AI助手'
          }
        }

        // 滚动到底部
        this.$nextTick(() => {
          this.scrollToBottom()
        })
      } catch (error) {
        console.error('加载会话失败:', error)
        this.$message.error('加载会话失败')
      } finally {
        this.pageLoading = false
      }
    },

    // 保存当前会话 - 修改为弹出确认框让用户编辑标题
    saveCurrentConversation(showTitlePrompt = false) {
      if (!this.currentConversationId) return

      // 准备保存的消息数据，移除不需要保存的临时属性
      const messagesForSave = this.chatMessages.map(msg => {
        // 创建消息的副本，避免修改原始对象
        const msgCopy = { ...msg }

        // 如果消息已被翻译，保存原始内容和翻译状态
        if (msg.isTranslated && msg.originalContent) {
          msgCopy.originalContent = msg.originalContent
          msgCopy.isTranslated = true
        }

        return msgCopy
      })

      // 生成默认标题（日期时间戳）
      const defaultTitle = this.generateConversationTitle()

      // 直接保存，不显示任何提示
      this.saveConversationWithTitle(messagesForSave, defaultTitle)
    },

    // 使用指定标题保存会话
    saveConversationWithTitle(messagesForSave, title) {
      try {
        // 首先检查存储空间
        this.checkAndCleanStorage()

        // 创建会话对象
        const conversation = {
          id: this.currentConversationId,
          title: title,
          messages: messagesForSave,
          model: this.currentModel ? {
            id: this.currentModel.id,
            name: this.currentModel.name
          } : null,
          timestamp: new Date().toISOString()
        }

        // 尝试保存到本地存储
        const storageKey = `${this.storageKeyPrefix}${this.currentConversationId}`
        const conversationData = JSON.stringify(conversation)

        // 检查单个会话大小
        const sizeInBytes = new Blob([conversationData]).size
        const sizeInMB = (sizeInBytes / (1024 * 1024)).toFixed(2)

        if (sizeInBytes > 5 * 1024 * 1024) { // 5MB单个会话限制
          this.$message.warning(`当前会话数据过大(${sizeInMB}MB)，建议创建新会话`)
          return
        }

        localStorage.setItem(storageKey, conversationData)

        // 更新会话列表
        this.updateConversationsList(conversation)
      } catch (error) {
        console.error('保存会话失败:', error)

        if (error.name === 'QuotaExceededError') {
          this.handleStorageQuotaExceeded()
        } else {
          this.$message.error('保存会话失败，请稍后重试')
        }
      }
    },

    // 检查并清理存储空间
    checkAndCleanStorage() {
      try {
        // 获取当前存储使用情况
        const storageInfo = this.getStorageUsage()

        // 如果存储使用率超过80%，自动清理
        if (storageInfo.usagePercentage > 80) {
          this.autoCleanOldConversations()
        }
      } catch (error) {
        console.error('检查存储空间失败:', error)
      }
    },

    // 获取存储使用情况
    getStorageUsage() {
      let totalSize = 0
      let aiChatSize = 0

      for (const key in localStorage) {
        if (localStorage.hasOwnProperty(key)) {
          const itemSize = localStorage.getItem(key).length
          totalSize += itemSize

          if (key.startsWith(this.storageKeyPrefix)) {
            aiChatSize += itemSize
          }
        }
      }

      // 估算localStorage最大容量 (通常为5-10MB)
      const estimatedMaxSize = 5 * 1024 * 1024 // 5MB
      const usagePercentage = (totalSize / estimatedMaxSize) * 100

      return {
        totalSize,
        aiChatSize,
        usagePercentage: Math.min(usagePercentage, 100),
        totalSizeMB: (totalSize / (1024 * 1024)).toFixed(2),
        aiChatSizeMB: (aiChatSize / (1024 * 1024)).toFixed(2)
      }
    },

    // 自动清理旧会话
    autoCleanOldConversations() {
      try {
        const listKey = `${this.storageKeyPrefix}list`
        const savedList = localStorage.getItem(listKey)

        if (!savedList) return

        const allConversations = JSON.parse(savedList)

        // 按时间排序，保留最新的50个会话
        allConversations.sort((a, b) => {
          return new Date(b.timestamp || b.createdAt) - new Date(a.timestamp || a.createdAt)
        })

        const maxConversations = 50
        const conversationsToDelete = allConversations.slice(maxConversations)

        if (conversationsToDelete.length > 0) {
          // 删除旧会话
          conversationsToDelete.forEach(conv => {
            const storageKey = `${this.storageKeyPrefix}${conv.id}`
            localStorage.removeItem(storageKey)
          })

          // 更新会话列表
          const remainingConversations = allConversations.slice(0, maxConversations)
          localStorage.setItem(listKey, JSON.stringify(remainingConversations))

          this.$message.info(`已自动清理 ${conversationsToDelete.length} 个旧会话以释放存储空间`)
        }
      } catch (error) {
        console.error('自动清理会话失败:', error)
      }
    },

    // 处理存储空间溢出
    handleStorageQuotaExceeded() {
      this.$confirm(
        '存储空间已满！请选择处理方式：',
        '存储空间不足',
        {
          confirmButtonText: '清理旧会话',
          cancelButtonText: '手动管理',
          type: 'warning',
          showClose: false,
          closeOnClickModal: false,
          closeOnPressEscape: false
        }
      ).then(() => {
        // 强制清理旧会话
        this.forceCleanOldConversations()
      }).catch(() => {
        // 打开存储管理对话框
        this.openStorageManagementDialog()
      })
    },

    // 强制清理旧会话
    forceCleanOldConversations() {
      try {
        const listKey = `${this.storageKeyPrefix}list`
        const savedList = localStorage.getItem(listKey)

        if (!savedList) return

        const allConversations = JSON.parse(savedList)

        // 只保留最新的20个会话
        allConversations.sort((a, b) => {
          return new Date(b.timestamp || b.createdAt) - new Date(a.timestamp || a.createdAt)
        })

        const maxConversations = 20
        const conversationsToDelete = allConversations.slice(maxConversations)

        // 删除旧会话
        conversationsToDelete.forEach(conv => {
          const storageKey = `${this.storageKeyPrefix}${conv.id}`
          localStorage.removeItem(storageKey)
        })

        // 更新会话列表
        const remainingConversations = allConversations.slice(0, maxConversations)
        localStorage.setItem(listKey, JSON.stringify(remainingConversations))

        // 重新加载历史会话
        this.loadHistoryPage(1)

        this.$message.success(`已清理 ${conversationsToDelete.length} 个旧会话，存储空间已释放`)

        // 重新尝试保存当前会话
        if (this.currentConversationId && this.chatMessages.length > 0) {
          setTimeout(() => {
            this.saveCurrentConversation()
          }, 500)
        }
      } catch (error) {
        console.error('强制清理会话失败:', error)
        this.$message.error('清理失败，请手动清理浏览器缓存')
      }
    },

    // 打开存储管理对话框
    openStorageManagementDialog() {
      const storageInfo = this.getStorageUsage()

      this.$alert(
        `<div style="text-align: left;">
          <p><strong>存储使用情况：</strong></p>
          <p>总存储大小：${storageInfo.totalSizeMB} MB</p>
          <p>AI聊天数据：${storageInfo.aiChatSizeMB} MB</p>
          <p>使用率：${storageInfo.usagePercentage.toFixed(1)}%</p>
          <br>
          <p><strong>建议操作：</strong></p>
          <p>1. 删除不需要的历史会话</p>
          <p>2. 清理浏览器缓存</p>
          <p>3. 创建新会话而不是继续长对话</p>
        </div>`,
        '存储管理',
        {
          dangerouslyUseHTMLString: true,
          confirmButtonText: '我知道了'
        }
      )
    },

    // 创建新会话
    createNewConversation() {
      if (!this.currentModel) {
        this.$message.warning('请先选择一个AI模型')
        return
      }

      // 创建新的会话ID
      const newId = Date.now().toString(36) + Math.random().toString(36).substr(2, 5)

      // 将当前会话保存（如果有）
      if (this.currentConversationId && this.chatMessages.length > 0) {
        // 使用带提示的保存方法
        this.saveCurrentConversation(true) // 显示标题编辑提示
      }

      // 创建新会话对象
      const modelName = this.currentModel ? this.currentModel.name : ''
      const newConversation = {
        id: newId,
        title: `${modelName} - 新对话`,
        createdAt: new Date().toISOString()
      }

      // 添加到会话列表
      this.conversations.unshift(newConversation)
      this.saveConversationList()

      // 清空当前消息
      this.chatMessages = []

      // 设置当前会话ID
      this.currentConversationId = newId

      this.$message.success('已创建新会话')
    },

    // 删除会话
    deleteConversation() {
      if (!this.currentConversationId) return

      this.$confirm('确定要删除此会话吗？此操作不可恢复', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      }).then(() => {
        // 从存储中移除
        const storageKey = `${this.storageKeyPrefix}${this.currentConversationId}`
        localStorage.removeItem(storageKey)

        // 从列表中移除
        const index = this.conversations.findIndex(c => c.id === this.currentConversationId)
        if (index !== -1) {
          this.conversations.splice(index, 1)
          this.saveConversationList()
        }

        // 如果还有其他会话，加载第一个
        if (this.conversations.length > 0) {
          this.currentConversationId = this.conversations[0].id
          this.loadConversation(this.currentConversationId)
        } else {
          // 否则清空当前会话
          this.currentConversationId = null
          this.chatMessages = []
        }

        this.$message.success('会话已删除')
      }).catch(() => {
        // 取消删除，不做任何操作
      })
    },

    // 初始化会话列表
    initConversations() {
      const listKey = `${this.storageKeyPrefix}list`
      const savedList = localStorage.getItem(listKey)

      if (savedList) {
        try {
          const allConversations = JSON.parse(savedList)
          this.historyPagination.total = allConversations.length

          // 只加载第一页的会话
          this.loadHistoryPage(1, allConversations)

          // 如果有会话，加载第一个
          if (this.conversations.length > 0) {
            this.currentConversationId = this.conversations[0].id
            // 注意：此时可能还没有加载模型数据，所以不立即加载会话
          }
        } catch (error) {
          console.error('加载会话列表失败', error)
          this.conversations = []
          this.historyPagination.total = 0
        }
      } else {
        this.conversations = []
        this.historyPagination.total = 0
      }
    },

    // 加载指定页的历史会话
    loadHistoryPage(page, allConversations = null) {
      this.historyLoading = true

      try {
        // 如果没有传入所有会话，则从localStorage获取
        if (!allConversations) {
          const listKey = `${this.storageKeyPrefix}list`
          const savedList = localStorage.getItem(listKey)
          if (!savedList) {
            this.conversations = []
            this.historyPagination.total = 0
            this.historyLoading = false
            return
          }
          allConversations = JSON.parse(savedList)
          this.historyPagination.total = allConversations.length
        }

        // 过滤会话（如果有搜索文本）
        let filteredConversations = allConversations
        if (this.historySearchText) {
          const searchText = this.historySearchText.toLowerCase()
          filteredConversations = allConversations.filter(conv =>
            conv.title && conv.title.toLowerCase().includes(searchText)
          )
          this.historyPagination.total = filteredConversations.length
        }

        // 计算当前页的起始和结束索引
        const start = (page - 1) * this.historyPagination.pageSize
        const end = start + this.historyPagination.pageSize

        // 获取当前页的会话
        this.conversations = filteredConversations.slice(start, end)
        this.historyPagination.currentPage = page
      } catch (error) {
        console.error('加载历史页面失败:', error)
        this.$message.error('加载历史会话失败')
      } finally {
        this.historyLoading = false
      }
    },

    // 处理历史会话搜索
    handleHistorySearch() {
      this.loadHistoryPage(1)
    },

    // 处理历史分页变化
    handleHistoryPageChange(page) {
      this.loadHistoryPage(page)
    },

    // 生成会话标题 - 使用节点名称作为默认名称
    generateConversationTitle() {
      // 使用日期时间戳作为标题
      const now = new Date()
      const year = now.getFullYear()
      const month = String(now.getMonth() + 1).padStart(2, '0')
      const day = String(now.getDate()).padStart(2, '0')
      const hours = String(now.getHours()).padStart(2, '0')
      const minutes = String(now.getMinutes()).padStart(2, '0')
      const seconds = String(now.getSeconds()).padStart(2, '0')

      // 格式：2025-01-30 15:30:45
      const timestamp = `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`

      // 如果有模型名称，加上模型前缀
      if (this.currentModel) {
        return `${this.currentModel.name} - ${timestamp}`
      }

      return timestamp
    },

    // 打开历史会话抽屉
    openHistoryDrawer() {
      this.historyDrawerVisible = true
      // 重置搜索和分页
      this.historySearchText = ''
      this.loadHistoryPage(1)
    },

    // 加载历史会话 - 修改为检查是否处于编辑状态
    loadHistoryConversation(row) {
      // 如果正在编辑标题，不加载会话
      if (row.isEditing) {
        return
      }

      this.currentConversationId = row.id
      this.loadConversation(this.currentConversationId)
      this.historyDrawerVisible = false
    },

    // 删除历史会话
    deleteHistoryConversation(row) {
      this.$confirm('确定要删除此历史会话吗？此操作不可恢复', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      }).then(() => {
        // 从列表中移除
        const index = this.conversations.findIndex(c => c.id === row.id)
        if (index !== -1) {
          this.conversations.splice(index, 1)
          this.saveConversationList()
        }

        // 如果还有其他会话，加载第一个
        if (this.conversations.length > 0) {
          this.currentConversationId = this.conversations[0].id
          this.loadConversation(this.currentConversationId)
        } else {
          // 否则清空当前会话
          this.currentConversationId = null
          this.chatMessages = []
        }

        this.$message.success('历史会话已删除')
      }).catch(() => {
        // 取消删除，不做任何操作
      })
    },

    // 格式化日期时间
    formatDateTime(isoString) {
      const date = new Date(isoString)
      return date.toLocaleString()
    },

    // 翻译消息
    async translateMessage(message) {
      if (this.translating) return

      try {
        this.translating = true
        // 设置消息的翻译状态为正在翻译
        this.$set(message, 'translating', true)

        // 保存原始内容
        if (!message.originalContent) {
          message.originalContent = message.content
        }

        // 调用翻译API
        const response = await Translate({
          text: message.content,
          source_language: 'auto',
          target_language: 'zh'
        })

        if (response.data) {
          // 更新消息内容为翻译后的内容
          message.content = response.data
          message.isTranslated = true

          this.$message.success('翻译成功')
        } else {
          this.$message.error('翻译失败，请稍后重试')
        }
      } catch (error) {
        console.error('翻译失败:', error)
        this.$message.error('翻译失败，请稍后重试')
      } finally {
        this.translating = false
        // 移除消息的翻译中状态
        this.$set(message, 'translating', false)
      }
    },

    // 显示原始消息
    showOriginalMessage(message) {
      if (message.originalContent) {
        message.content = message.originalContent
        message.isTranslated = false
      }
    },

    // 根据ID查找节点
    findNodeById(id) {
      // 递归查找函数
      const findNode = (nodes) => {
        if (!nodes || nodes.length === 0) return null

        for (const node of nodes) {
          if (node.id === id) return node

          // 如果有子节点，递归查找
          if (node.children && node.children.length > 0) {
            const found = findNode(node.children)
            if (found) return found
          }
        }

        return null
      }

      return findNode(this.treeData)
    },

    // 分享当前节点
    shareCurrentNode() {
      if (!this.currentModel || !this.currentModel.id) {
        this.$message.warning('请先选择一个节点')
        return
      }

      try {
        // 获取当前页面URL
        const currentUrl = window.location.href.split('?')[0]
        // 生成包含节点ID的分享链接
        const shareUrl = `${currentUrl}?nodeId=${this.currentModel.id}`

        // 复制到剪贴板
        if (navigator.clipboard && navigator.clipboard.writeText) {
          navigator.clipboard.writeText(shareUrl).then(() => {
            this.$message.success({
              message: `节点分享链接已复制到剪贴板！\n其他用户打开链接后将自动选择节点：${this.currentModel.name}`,
              duration: 3000,
              showClose: true
            })
          }).catch(() => {
            this.fallbackCopyToClipboard(shareUrl)
          })
        } else {
          this.fallbackCopyToClipboard(shareUrl)
        }
      } catch (error) {
        console.error('生成分享链接失败:', error)
        this.$message.error('生成分享链接失败')
      }
    },

    // 备用复制方法
    fallbackCopyToClipboard(text) {
      const textArea = document.createElement('textarea')
      textArea.value = text
      textArea.style.position = 'fixed'
      textArea.style.opacity = '0'
      document.body.appendChild(textArea)
      textArea.select()
      try {
        document.execCommand('copy')
        this.$message.success({
          message: `节点分享链接已复制到剪贴板！\n其他用户打开链接后将自动选择节点：${this.currentModel.name}`,
          duration: 3000,
          showClose: true
        })
      } catch (err) {
        console.error('复制失败:', err)
        this.$message.error('复制失败，请手动复制')
      } finally {
        document.body.removeChild(textArea)
      }
    },

    // 检查URL参数并自动选择节点
    checkAndSelectNodeFromUrl() {
      // 从URL中获取nodeId参数
      const urlParams = new URLSearchParams(window.location.search)
      const nodeId = urlParams.get('nodeId')

      if (nodeId) {
        // 等待树形数据加载完成后选择节点
        const checkTreeData = setInterval(() => {
          if (this.treeData && this.treeData.length > 0) {
            clearInterval(checkTreeData)

            // 查找节点
            const node = this.findNodeById(parseInt(nodeId))
            if (node) {
              // 自动选择节点
              this.$nextTick(() => {
                this.handleNodeClick(node)
                this.$message.success(`已自动选择分享的节点：${node.name}`)

                // 如果树形选择器是折叠状态，自动展开
                if (this.treeCollapsed) {
                  this.treeCollapsed = false
                }

                // 清除URL参数（可选，保持URL简洁）
                // window.history.replaceState({}, document.title, window.location.pathname)
              })
            } else {
              this.$message.warning('未找到分享的节点，可能已被删除或修改')
            }
          }
        }, 100)

        // 设置超时，防止无限等待
        setTimeout(() => {
          clearInterval(checkTreeData)
        }, 5000)
      }
    },

    // 更新会话列表
    updateConversationsList(conversation) {
      // 直接调用保存会话列表方法
      this.saveConversationList()
    },

    // 保存会话列表
    saveConversationList() {
      const listKey = `${this.storageKeyPrefix}list`
      try {
        // 获取完整的会话列表
        let allConversations = []
        const savedList = localStorage.getItem(listKey)
        if (savedList) {
          allConversations = JSON.parse(savedList)
        }

        // 查找当前更新的会话
        const index = allConversations.findIndex(c => c.id === this.currentConversationId)

        // 更新或添加会话
        if (index !== -1) {
          // 更新现有会话
          allConversations[index] = {
            ...allConversations[index],
            title: this.generateConversationTitle(),
            timestamp: new Date().toISOString()
          }
        } else if (this.currentConversationId) {
          // 添加新会话
          allConversations.push({
            id: this.currentConversationId,
            title: this.generateConversationTitle(),
            createdAt: new Date().toISOString(),
            timestamp: new Date().toISOString()
          })
        }

        // 按时间戳排序，最新的在前面
        allConversations.sort((a, b) => {
          return new Date(b.timestamp || b.createdAt) - new Date(a.timestamp || a.createdAt)
        })

        // 保存完整列表
        localStorage.setItem(listKey, JSON.stringify(allConversations))

        // 更新分页信息
        this.historyPagination.total = allConversations.length

        // 如果当前正在查看历史，刷新当前页
        if (this.historyDrawerVisible) {
          this.loadHistoryPage(this.historyPagination.currentPage)
        }
      } catch (error) {
        console.error('保存会话列表失败', error)
      }
    },

    // 处理表格选择变化
    handleSelectionChange(selection) {
      this.selectedConversations = selection
    },

    // 批量删除历史会话
    batchDeleteHistoryConversations() {
      if (this.selectedConversations.length === 0) return

      this.$confirm(`确定要删除选中的 ${this.selectedConversations.length} 个会话吗？此操作不可恢复`, '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      }).then(() => {
        // 获取所有会话
        const listKey = `${this.storageKeyPrefix}list`
        const savedList = localStorage.getItem(listKey)
        if (!savedList) return

        let allConversations = JSON.parse(savedList)
        const selectedIds = this.selectedConversations.map(c => c.id)

        // 从存储中删除每个会话
        selectedIds.forEach(id => {
          localStorage.removeItem(`${this.storageKeyPrefix}${id}`)
        })

        // 从列表中过滤掉已删除的会话
        allConversations = allConversations.filter(c => !selectedIds.includes(c.id))

        // 保存更新后的列表
        localStorage.setItem(listKey, JSON.stringify(allConversations))

        // 如果当前会话在删除列表中，清空当前会话
        if (selectedIds.includes(this.currentConversationId)) {
          this.currentConversationId = null
          this.chatMessages = []
        }

        // 重新加载当前页
        this.loadHistoryPage(this.historyPagination.currentPage)

        this.$message.success(`已删除 ${selectedIds.length} 个会话`)
      }).catch(() => {
        // 取消删除，不做任何操作
      })
    },

    // 打开设置对话框
    openSettingsDialog() {
      // 复制当前设置到临时对象
      this.settings = {
        maxMessagesPerConversation: this.maxMessagesPerConversation,
        autoCreateNewConversation: this.autoCreateNewConversation
      }

      // 刷新存储统计
      this.refreshStorageStats()

      this.settingsDialogVisible = true
    },

    // 保存设置
    saveSettings() {
      // 更新设置
      this.maxMessagesPerConversation = this.settings.maxMessagesPerConversation
      this.autoCreateNewConversation = this.settings.autoCreateNewConversation

      // 保存到本地存储
      try {
        localStorage.setItem('aisop_chat_settings', JSON.stringify({
          maxMessagesPerConversation: this.maxMessagesPerConversation,
          autoCreateNewConversation: this.autoCreateNewConversation
        }))
        this.$message.success('设置已保存')
      } catch (error) {
        console.error('保存设置失败:', error)
        this.$message.error('保存设置失败')
      }

      this.settingsDialogVisible = false
    },

    // 加载设置
    loadSettings() {
      try {
        const savedSettings = localStorage.getItem('aisop_chat_settings')
        if (savedSettings) {
          const settings = JSON.parse(savedSettings)
          this.maxMessagesPerConversation = settings.maxMessagesPerConversation || 30
          this.autoCreateNewConversation = settings.autoCreateNewConversation !== undefined
            ? settings.autoCreateNewConversation : true
        }
      } catch (error) {
        console.error('加载设置失败:', error)
      }
    },

    // 检测内容是否为HTML代码
    isHtmlCode(content) {
      if (!content || typeof content !== 'string') return false

      // 检查常见的HTML标签模式
      const htmlPattern = /^\s*<!DOCTYPE|<html|<head|<body|<div|<script|<style/i

      // 如果内容看起来像HTML源代码
      return htmlPattern.test(content.trim())
    },

    renderMarkdown(content) {
      // 如果内容是HTML代码，显示为代码块
      if (this.isHtmlCode(content)) {
        // 生成唯一ID用于引用
        const codeId = 'html-code-' + Date.now()

        // 转义HTML字符以防止执行
        const escapedHtml = this.escapeHtml(content)

        // 创建黑色背景代码显示块
        return `
          <div class="code-block-container">
            <div class="code-block-header">
              <span>HTML</span>
              <div class="code-block-actions">
                <button onclick="navigator.clipboard.writeText(document.getElementById('${codeId}').textContent)" title="复制代码">
                  <i class="el-icon-document-copy"></i> 复制
                </button>
                <button onclick="(function(){
                  const win = window.open('', '_blank');
                  win.document.write(document.getElementById('${codeId}').textContent);
                  win.document.close();
                })()" title="运行HTML">
                  <i class="el-icon-video-play"></i> 运行HTML
                </button>
              </div>
            </div>
            <pre class="code-block"><code id="${codeId}">${escapedHtml}</code></pre>
          </div>
        `
      }

      // 检查markdown中是否有HTML代码块
      const htmlCodeBlockRegex = /```(?:html|HTML)([\s\S]*?)```/g
      if (htmlCodeBlockRegex.test(content)) {
        // 处理所有HTML代码块
        const processedContent = content.replace(htmlCodeBlockRegex, (match, codeContent) => {
          const codeId = 'html-code-' + Date.now() + '-' + Math.random().toString(36).substr(2, 9)
          const escapedHtml = this.escapeHtml(codeContent.trim())

          return `
          <div class="code-block-container">
            <div class="code-block-header">
              <span>HTML</span>
              <div class="code-block-actions">
                <button onclick="navigator.clipboard.writeText(document.getElementById('${codeId}').textContent)" title="复制代码">
                  <i class="el-icon-document-copy"></i> 复制
                </button>
                <button onclick="(function(){
                  const win = window.open('', '_blank');
                  win.document.write(document.getElementById('${codeId}').textContent);
                  win.document.close();
                })()" title="运行HTML">
                  <i class="el-icon-video-play"></i> 运行HTML
                </button>
              </div>
            </div>
            <pre class="code-block"><code id="${codeId}">${escapedHtml}</code></pre>
          </div>
          `
        })

        // 原有的Markdown渲染逻辑处理其他内容
        try {
          const customMd = new MarkdownIt({
            html: true,
            breaks: true,
            linkify: true,
            typographer: false,
            // 重要: 禁用默认的代码块渲染
            highlight: function(str, lang) {
              // 跳过HTML代码块，因为我们已经处理过了
              if (lang && lang.toLowerCase() === 'html') {
                return '' // 返回空字符串，因为我们已经处理了
              }
              return '' // 其他语言使用默认处理
            }
          })

          // 添加插件处理链接，使其在新窗口打开并添加颜色样式
          customMd.renderer.rules.link_open = (tokens, idx, options, env, self) => {
            const token = tokens[idx]
            token.attrPush(['target', '_blank'])
            token.attrPush(['rel', 'noopener noreferrer'])
            token.attrPush(['style', 'color: #409EFF !important; text-decoration: underline !important;'])
            return self.renderToken(tokens, idx, options)
          }

          // 图片添加css样式自适应宽度和点击预览功能
          customMd.renderer.rules.image = (tokens, idx, options, env, self) => {
            const token = tokens[idx]
            // 更优化的图片自适应样式
            token.attrPush(['style', 'max-width: 100%; max-height: 60vh; width: auto; height: auto; object-fit: contain; display: block; margin: 10px auto; cursor: pointer; border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.1);'])
            token.attrPush(['class', 'markdown-image-clickable'])
            token.attrPush(['title', '点击预览图片'])
            token.attrPush(['loading', 'lazy']) // 添加懒加载
            return self.renderToken(tokens, idx, options)
          }

          return customMd.render(processedContent || '')
        } catch (error) {
          console.error('Markdown渲染失败:', error)
          return processedContent
            .replace(/</g, '&lt;')
            .replace(/>/g, '&gt;')
            .replace(/\n/g, '<br>')
        }
      }

      // 原有的Markdown渲染逻辑
      try {
      // 配置markdown-it以便在新窗口中打开链接
        const customMd = new MarkdownIt({
          html: true,
          breaks: true,
          linkify: true,
          // 添加更安全的选项
          typographer: false, // 关闭排版功能，避免特殊字符干扰
          highlight: function(str, lang) {
            // 为HTML代码块添加特殊处理
            if (lang && lang.toLowerCase() === 'html') {
              const codeId = 'html-code-' + Date.now() + '-' + Math.random().toString(36).substr(2, 9)
              const escapedHtml = str
                .replace(/&/g, '&amp;')
                .replace(/</g, '&lt;')
                .replace(/>/g, '&gt;')
                .replace(/"/g, '&quot;')
                .replace(/'/g, '&#39;')

              return `
              <div class="code-block-container">
                <div class="code-block-header">
                  <span>HTML</span>
                  <div class="code-block-actions">
                    <button onclick="navigator.clipboard.writeText(document.getElementById('${codeId}').textContent)" title="复制代码">
                      <i class="el-icon-document-copy"></i> 复制
                    </button>
                    <button onclick="(function(){
                      const win = window.open('', '_blank');
                      win.document.write(document.getElementById('${codeId}').textContent);
                      win.document.close();
                    })()" title="运行HTML">
                      <i class="el-icon-video-play"></i> 运行HTML
                    </button>
                  </div>
                </div>
                <pre class="code-block"><code id="${codeId}">${escapedHtml}</code></pre>
              </div>
              `
            }
            return '' // 对于其他语言使用默认
          }
        })

        // 添加插件处理链接，使其在新窗口中打开并添加颜色样式
        customMd.renderer.rules.link_open = (tokens, idx, options, env, self) => {
          const token = tokens[idx]
          // 添加target属性和样式
          token.attrPush(['target', '_blank'])
          token.attrPush(['rel', 'noopener noreferrer'])
          token.attrPush(['style', 'color: #409EFF !important; text-decoration: underline !important;'])

          return self.renderToken(tokens, idx, options)
        }
        // 图片添加css样式自适应宽度和点击预览功能
        customMd.renderer.rules.image = (tokens, idx, options, env, self) => {
          const token = tokens[idx]
          // 给图片添加类名和样式，实现自适应宽度和点击预览
          token.attrPush(['style', 'max-width: 100%; max-height: 60vh; width: auto; height: auto; object-fit: contain; display: block; margin: 10px auto; cursor: pointer; border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.1);'])
          token.attrPush(['class', 'markdown-image-clickable'])
          token.attrPush(['title', '点击预览图片'])
          token.attrPush(['loading', 'lazy']) // 添加懒加载
          return self.renderToken(tokens, idx, options)
        }

        return customMd.render(content || '')
      } catch (error) {
        console.error('Markdown渲染失败:', error)
        // 失败时返回原始内容，确保内容不会丢失
        return content
          .replace(/</g, '&lt;')
          .replace(/>/g, '&gt;')
          .replace(/\n/g, '<br>')
      }
    },

    // 在sendMessage方法中更新消息内容之前添加的安全处理函数
    // 可以添加到methods中
    safeUpdateContent(messageIndex, content) {
      try {
        // 尝试更新消息内容
        this.chatMessages[messageIndex].content = content
      } catch (error) {
        console.error('更新消息内容时出错:', error)
        // 如果出错，使用更安全的方式更新
        this.$set(this.chatMessages[messageIndex], 'content', String(content))
      }

      // 更新内容后，尝试滚动到底部
      this.scrollToBottom(false)
    },

    // 添加到 methods 中
    handleFetchError(error, assistantMessageIndex) {
      console.error('请求处理错误:', error)

      // 根据错误类型处理
      if (error.name === 'AbortError') {
        // 处理中止错误
        const reason = error.message || '未知原因'
        console.log(`请求被中止，原因: ${reason}`)

        if (this.isTimeout) {
          let timeoutMessage
          if (error.message === 'initial_timeout') {
            timeoutMessage = '[AI响应超时，请稍后重试]'
          } else if (error.message === 'streaming_timeout') {
            // 检查是否已接收到部分内容
            const currentContent = this.chatMessages[assistantMessageIndex].content || ''
            if (currentContent.length > 10) {
              timeoutMessage = '[数据传输中断，但已接收部分内容]'
            } else {
              timeoutMessage = '[AI响应超时，请稍后重试]'
            }
          } else {
            timeoutMessage = '[请求超时，请检查网络后重试]'
          }

          // 更新消息内容
          const currentContent = this.chatMessages[assistantMessageIndex].content || ''
          if (!currentContent.includes(timeoutMessage)) {
            this.safeUpdateContent(assistantMessageIndex, currentContent + `\n\n${timeoutMessage}`)
          }

          this.$message.warning(timeoutMessage.replace(/\[|\]/g, ''))
          this.isTimeout = false
        } else if (error.message === 'user_cancel') {
          // 用户主动取消
          this.safeUpdateContent(
            assistantMessageIndex,
            (this.chatMessages[assistantMessageIndex].content || '') + '\n\n[用户已终止回复]'
          )
        } else if (error.message === 'component_destroyed') {
          // 组件销毁，不需要UI提示
          console.log('组件销毁导致请求中止')
        } else {
          // 其他原因的中止
          const currentContent = this.chatMessages[assistantMessageIndex].content || ''

          // 避免重复添加中断提示
          if (!currentContent.includes('[请求被中断]')) {
            this.safeUpdateContent(assistantMessageIndex, currentContent + '\n\n[请求被中断]')
          }
        }
      } else if (error.message && error.message.includes('network')) {
        // 网络错误
        this.$message.error('网络连接异常，请检查您的网络')
        this.safeUpdateContent(
          assistantMessageIndex,
          (this.chatMessages[assistantMessageIndex].content || '') + '\n\n[网络连接异常，请检查您的网络后重试]'
        )
      } else {
        // 处理其他类型的错误
        this.$message.error('发送消息失败')

        // 避免覆盖可能已经接收到的部分内容
        const currentContent = this.chatMessages[assistantMessageIndex].content || ''
        if (currentContent.length > 10) {
          this.safeUpdateContent(
            assistantMessageIndex,
            currentContent + '\n\n[发生错误，但已显示部分内容]'
          )
        } else {
          this.safeUpdateContent(assistantMessageIndex, '抱歉，发生了错误，请稍后再试。')
        }
      }
    },

    // 添加预处理Markdown的方法
    preprocessMarkdown(text) {
      if (!text) return ''

      try {
        // 处理特殊Markdown字符，尤其是##等标题标记

        // 1. 处理标题 - 确保#后面有空格
        let processed = text.replace(/^(#{1,6})([^#\s])/gm, '$1 $2')

        // 如果接收到的仅是##，将其转换为文本
        if (text.trim() === '##') {
          return '\\#\\#'
        }

        // 2. 处理不完整的代码块
        const codeBlockMarkers = (text.match(/```/g) || []).length
        if (codeBlockMarkers > 0 && codeBlockMarkers % 2 !== 0) {
          processed = processed.replace(/```(?!.*```)/g, '\\`\\`\\`')
        }

        return processed
      } catch (error) {
        console.error('预处理Markdown时出错:', error)
        // 出错时，返回原始文本但转义可能导致问题的字符
        return text.replace(/([#`])/g, '\\$1')
      }
    },

    // 添加复制选中文本的方法
    copySelectedText() {
      try {
        const selection = window.getSelection()
        if (!selection.toString()) {
          this.$message.warning('请先选择要复制的文本')
          return
        }

        // 尝试使用现代API
        if (navigator.clipboard && navigator.clipboard.writeText) {
          navigator.clipboard.writeText(selection.toString())
            .then(() => {
              this.$message.success('选中内容已复制')
            })
            .catch(err => {
              console.error('复制选中内容失败:', err)
              // 备用方法
              document.execCommand('copy')
              this.$message.success('选中内容已复制')
            })
        } else {
          // 老浏览器兼容
          document.execCommand('copy')
          this.$message.success('选中内容已复制')
        }
      } catch (err) {
        console.error('复制选中内容失败:', err)
        this.$message.error('复制失败，请手动复制')
      }
    },

    // 将 copyHtmlContent 方法重命名为 copyFormattedContent，避免与全局方法冲突
    // 添加一个辅助方法来检测和使用最佳的复制方法
    copyFormattedContent(html, text) {
      // 使用检查方法
      if (this.isClipboardAPISupported()) {
        try {
          // 创建包含HTML和纯文本的剪贴板项
          const htmlBlob = new Blob([html], { type: 'text/html' })
          const textBlob = new Blob([text || html.replace(/<[^>]*>/g, '')], { type: 'text/plain' })

          return navigator.clipboard.write([
            new window.ClipboardItem({
              'text/html': htmlBlob,
              'text/plain': textBlob
            })
          ])
        } catch (err) {
          console.warn('高级剪贴板API失败，使用备用方法:', err)
          return this.fallbackCopyHtml(html)
        }
      } else {
        // 如果不支持，使用备用方法
        return this.fallbackCopyHtml(html)
      }
    },

    // 备用的HTML复制方法
    fallbackCopyHtml(html) {
      return new Promise((resolve, reject) => {
        try {
          // 创建临时元素
          const tempDiv = document.createElement('div')
          tempDiv.innerHTML = html
          tempDiv.style.position = 'fixed'
          tempDiv.style.left = '-9999px'
          tempDiv.setAttribute('contenteditable', 'true')
          document.body.appendChild(tempDiv)

          // 选择内容
          const selection = window.getSelection()
          const range = document.createRange()
          range.selectNodeContents(tempDiv)
          selection.removeAllRanges()
          selection.addRange(range)

          // 执行复制
          const successful = document.execCommand('copy')

          // 清理
          selection.removeAllRanges()
          document.body.removeChild(tempDiv)

          if (successful) {
            resolve()
          } else {
            reject(new Error('execCommand复制失败'))
          }
        } catch (err) {
          reject(err)
        }
      })
    },

    // 开始编辑标题
    startTitleEdit(conversation) {
      // 设置编辑状态
      this.$set(conversation, 'isEditing', true)
      this.$set(conversation, 'editingTitle', conversation.title)

      // 在下一个周期聚焦输入框
      this.$nextTick(() => {
        const inputRefs = this.$refs.titleInput
        if (Array.isArray(inputRefs)) {
          // 可能有多个引用，找到匹配当前会话的那个
          for (const inputRef of inputRefs) {
            if (inputRef.$parent.$parent.row.id === conversation.id) {
              inputRef.focus()
              break
            }
          }
        } else if (inputRefs) {
          inputRefs.focus()
        }
      })
    },

    // 完成标题编辑
    finishTitleEdit(conversation) {
      // 检查标题是否有效
      if (!conversation.editingTitle || !conversation.editingTitle.trim()) {
        this.$message.warning('标题不能为空')
        return
      }

      // 更新标题
      conversation.title = conversation.editingTitle.trim()
      conversation.isEditing = false

      // 保存更新的标题
      this.updateConversationTitle(conversation.id, conversation.title)
    },

    // 更新会话标题 - 修复标题更新无效的问题
    updateConversationTitle(conversationId, newTitle) {
      try {
        // 从存储中获取会话
        const storageKey = `${this.storageKeyPrefix}${conversationId}`
        const conversationJson = localStorage.getItem(storageKey)

        if (!conversationJson) {
          console.error('无法找到会话:', conversationId)
          return
        }

        // 解析会话数据
        const conversation = JSON.parse(conversationJson)

        // 更新标题
        conversation.title = newTitle

        // 更新时间戳，确保排序正确
        conversation.timestamp = new Date().toISOString()

        // 保存回存储
        localStorage.setItem(storageKey, JSON.stringify(conversation))

        // 同时更新列表中的数据
        const listKey = `${this.storageKeyPrefix}list`
        const savedList = localStorage.getItem(listKey)
        if (savedList) {
          const allConversations = JSON.parse(savedList)

          // 查找并更新对应会话的标题
          const convIndex = allConversations.findIndex(c => c.id === conversationId)
          if (convIndex !== -1) {
            allConversations[convIndex].title = newTitle
            allConversations[convIndex].timestamp = conversation.timestamp

            // 保存更新后的列表
            localStorage.setItem(listKey, JSON.stringify(allConversations))
          }
        }

        // 如果是当前会话，也需要更新当前会话的标题
        if (this.currentConversationId === conversationId) {
          // 如果有需要，可以在此添加当前会话的标题更新逻辑
        }

        this.$message.success('标题已更新')
      } catch (error) {
        console.error('更新会话标题失败:', error)
        this.$message.error('更新标题失败')
      }
    },

    // 添加一个检查方法
    isClipboardAPISupported() {
      return (
        // 检查基本的clipboard API
        navigator.clipboard &&
        navigator.clipboard.write &&
        // 检查ClipboardItem是否存在
        typeof window.ClipboardItem !== 'undefined'
      )
    },

    beforeUpload(file) {
      if (!this.currentModel) {
        this.$message.warning('请先选择一个AI模型')
        return false
      }

      // 检查文件类型, 增加支持zip
      const validTypes = {
        'image/jpeg': '图片',
        'image/jpg': '图片',
        'image/png': '图片',
        'application/zip': 'zip',
        'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': 'Excel',
        'text/plain': '文本'
      }

      if (!validTypes[file.type]) {
        this.$message.error('不支持的文件类型，请上传图片(.jpg,.jpeg,.png)、Excel(.xlsx)或文本(.txt)文件, zip')
        return false
      }

      // 检查文件大小
      const maxSize = 10 * 1024 * 1024 // 10MB
      if (file.size > maxSize) {
        this.$message.error('文件大小不能超过10MB')
        return false
      }

      // 显示加载中
      this.pageLoading = true

      // 存储文件对象，用于后续处理
      const fileName = file.uid
      this.listObj[fileName] = {
        hasSuccess: false,
        uid: file.uid,
        name: file.name,
        type: this.getFileType(file)
      }

      return true // 允许上传
    },

    handleUploadSuccess(response, file, fileList) {
      this.pageLoading = false
      const uid = file.uid
      console.log('上传成功响应:', response)
      console.log('文件类型:', this.getFileType(file))
      console.log('文件URL:', response.file_url)

      if (response.success) {
        // 记录上传成功
        if (this.listObj[uid]) {
          this.listObj[uid].hasSuccess = true
          this.listObj[uid].url = response.file_url
        }

        // 保存上传的文件信息
        const fileName = response.original_name || file.name
        const displayName = this.formatFileName(fileName) // 使用格式化后的文件名
        const fileType = this.getFileType(file)

        this.uploadedFiles.push({
          original_name: fileName,
          display_name: displayName, // 添加用于显示的名称
          file_name: response.file_name || file.name,
          file_url: response.file_url,
          file_type: fileType,
          uid: uid,
          size: file.size,
          selected: false
        })

        console.log('添加到uploadedFiles:', {
          file_type: fileType,
          file_url: response.file_url,
          display_name: displayName
        })

        // 根据文件类型生成内容，并保存到uploadedFiles中
        let fileContent = ''
        if (fileType === '图片') {
          // 图片使用Markdown格式
          fileContent = `![${displayName}](${response.file_url})`
        } else if (fileType === 'Excel') {
          // Excel文件
          fileContent = `已上传Excel文件: [${displayName}](${response.file_url})\n文件大小: ${this.formatFileSize(file.size)}`
        } else if (fileType === '文本') {
          // 文本文件
          fileContent = `文件: [${displayName}](${response.file_url})\n文件类型: 文本文件`
        } else {
          // 其他文件
          fileContent = `文件: [${displayName}](${response.file_url})`
        }

        // 更新最后一个上传的文件的content字段
        const lastIndex = this.uploadedFiles.length - 1
        if (lastIndex >= 0) {
          this.uploadedFiles[lastIndex].content = fileContent
        }

        this.$message.success(`已添加${fileType || '文件'}：${displayName}`)
      } else {
        this.$message.error(`文件上传失败: ${response.msg || '服务器错误'}`)
      }
    },

    handleUploadError(error, file, fileList) {
      this.pageLoading = false
      console.error('文件上传失败:', error)
      this.$message.error('文件上传失败，请检查网络连接后重试')
    },

    getFileType(file) {
      const typeMap = {
        'image/jpeg': '图片',
        'image/jpg': '图片',
        'image/png': '图片',
        'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': 'Excel',
        'text/plain': '文本'
      }

      return typeMap[file.type] || '未知类型'
    },

    // 格式化文件大小
    formatFileSize(bytes) {
      if (bytes === 0) return '0 B'
      const k = 1024
      const sizes = ['B', 'KB', 'MB', 'GB']
      const i = Math.floor(Math.log(bytes) / Math.log(k))
      return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]
    },

    // 预览文件
    previewFile(file) {
      if (file.file_type === '图片') {
        // 使用Element UI的图片预览
        this.$alert(
          `<div style="text-align:center"><img src="${file.file_url}" style="max-width:100%;max-height:500px;"></div>`,
          file.original_name,
          {
            dangerouslyUseHTMLString: true,
            customClass: 'image-preview-dialog',
            showConfirmButton: false,
            callback: () => {}
          }
        )
      } else {
        // 其他文件类型，直接在新窗口打开
        window.open(file.file_url, '_blank')
      }
    },

    // 移除文件
    removeFile(uid) {
      this.$confirm('确定要移除该文件吗?', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      }).then(() => {
        // 根据uid查找并删除文件
        const index = this.uploadedFiles.findIndex(file => file.uid === uid)
        if (index !== -1) {
          this.uploadedFiles.splice(index, 1)
          this.$message.success('文件已移除')
        }
      }).catch(() => {})
    },

    // 格式化文件名，显示前后几位，中间用...代替
    formatFileName(fileName, maxLength = 20) {
      if (!fileName) return ''
      if (fileName.length <= maxLength) return fileName

      // 获取文件扩展名
      const lastDotIndex = fileName.lastIndexOf('.')
      const ext = lastDotIndex !== -1 ? fileName.substring(lastDotIndex) : ''

      // 计算前缀长度
      const prefixLength = Math.floor((maxLength - 3 - ext.length) / 2)
      // 计算后缀长度(不包括扩展名)
      const suffixLength = maxLength - 3 - prefixLength - ext.length

      // 截取前缀和后缀
      const prefix = fileName.substring(0, prefixLength)
      const suffix = lastDotIndex !== -1
        ? fileName.substring(lastDotIndex - suffixLength, lastDotIndex)
        : fileName.substring(fileName.length - suffixLength)

      return `${prefix}...${suffix}${ext}`
    },

    previewImage(url) {
      // 使用Element UI的图片预览组件
      this.currentPreviewImage = url
      this.imagePreviewVisible = true
    },

    downloadFile(url, filename) {
      // 创建下载链接
      const link = document.createElement('a')
      link.href = url
      link.download = filename
      link.target = '_blank'
      document.body.appendChild(link)
      link.click()
      document.body.removeChild(link)
    },

    // 为markdown图片添加点击事件监听
    addMarkdownImageClickListeners() {
      const chatContainer = this.$refs.chatContainerRef
      if (chatContainer) {
        // 使用事件委托监听所有markdown图片点击事件
        chatContainer.addEventListener('click', (event) => {
          if (event.target.tagName === 'IMG' && event.target.classList.contains('markdown-image-clickable')) {
            event.preventDefault()
            this.previewImage(event.target.src)
          }
        })
      }
    },

    // 复制图片链接
    copyImageUrl() {
      if (this.currentPreviewImage) {
        // 创建临时文本域
        const textarea = document.createElement('textarea')
        textarea.value = this.currentPreviewImage
        textarea.style.position = 'fixed'
        textarea.style.opacity = '0'
        document.body.appendChild(textarea)
        textarea.select()

        try {
          const successful = document.execCommand('copy')
          if (successful) {
            this.$message.success('图片链接已复制到剪贴板')
          } else {
            this.$message.error('复制失败，请手动复制')
          }
        } catch (err) {
          console.error('复制失败:', err)
          this.$message.error('复制失败，请手动复制')
        } finally {
          document.body.removeChild(textarea)
        }
      }
    },

    // 将图片作为附件添加
    addImageAsAttachment() {
      if (this.currentPreviewImage) {
        // 从URL中提取文件名，处理可能的查询参数
        let url = this.currentPreviewImage
        // 移除查询参数
        if (url.indexOf('?') !== -1) {
          url = url.substring(0, url.indexOf('?'))
        }
        // 移除hash
        if (url.indexOf('#') !== -1) {
          url = url.substring(0, url.indexOf('#'))
        }

        const urlParts = url.split('/')
        let fileName = urlParts[urlParts.length - 1] || 'image.jpg'

        // 确保文件名有图片扩展名
        const imageExtensions = ['.jpg', '.jpeg', '.png', '.gif', '.bmp', '.webp']
        const hasImageExt = imageExtensions.some(ext => fileName.toLowerCase().endsWith(ext))
        if (!hasImageExt) {
          // 如果没有扩展名或扩展名不是图片格式，添加.jpg
          fileName = fileName.indexOf('.') === -1 ? fileName + '.jpg' : fileName.replace(/\.[^.]+$/, '.jpg')
        }

        // 生成唯一ID
        const uid = Date.now() + '_' + Math.random().toString(36).substr(2, 9)

        // 创建附件对象，保持与现有文件结构一致
        const imageAttachment = {
          uid: uid,
          file_name: fileName, // 使用file_name而不是name
          display_name: fileName,
          original_name: fileName,
          file_url: this.currentPreviewImage, // 使用file_url保持一致
          type: 'image',
          size: 0, // 图片URL无法获取真实大小
          content: `![${fileName}](${this.currentPreviewImage})` // Markdown格式的图片
        }

        // 添加到附件列表
        imageAttachment.selected = false
        this.uploadedFiles.push(imageAttachment)

        // 关闭预览弹窗
        this.imagePreviewVisible = false

        this.$message.success(`已将图片添加为附件：${fileName}`)
      }
    },

    handleImageError(event) {
      // 图片加载失败时显示占位图标
      const img = event.target
      img.style.display = 'none'
      const parent = img.parentElement
      parent.innerHTML = '<i class="el-icon-picture-outline-round" style="font-size: 24px; color: #ccc;"></i>'
      parent.style.cursor = 'default'
    },

    isImageFile(fileName) {
      // 检查fileName是否存在
      if (!fileName) return false

      // 根据文件扩展名判断是否为图片
      const imageExtensions = ['.jpg', '.jpeg', '.png', '.gif', '.bmp', '.webp']
      const ext = fileName.toLowerCase().substring(fileName.lastIndexOf('.'))
      return imageExtensions.includes(ext)
    },

    isExcelFile(fileName) {
      // 检查fileName是否存在
      if (!fileName) return false

      // 根据文件扩展名判断是否为Excel文件
      const excelExtensions = ['.xlsx', '.xls']
      const ext = fileName.toLowerCase().substring(fileName.lastIndexOf('.'))
      return excelExtensions.includes(ext)
    },

    handleImageUploaded(imageData) {
      // 处理AIChatInput上传的图片，将其添加到附件列表
      console.log('AIChatInput上传的图片数据:', imageData)

      if (imageData && imageData.url) {
        const timestamp = new Date().getTime()
        const uid = `chat_${timestamp}`

        // 从URL中提取文件名或使用提供的文件名
        const fileName = imageData.name || (() => {
          const urlParts = imageData.url.split('/')
          return urlParts[urlParts.length - 1] || `chat_image_${timestamp}.png`
        })()
        const displayName = this.formatFileName(fileName)

        // 添加到附件列表
        this.uploadedFiles.push({
          original_name: fileName,
          display_name: displayName,
          file_name: fileName,
          file_url: imageData.url,
          uid: uid,
          size: imageData.size || 0,
          selected: false
        })

        this.$message.success('图片已添加到附件列表')
      }
    },

    // 获取简化的文件名（前4个单词）
    getShortFileName(file) {
      const fullName = file.display_name || file.original_name || ''
      const words = fullName.split(/[-_.\s]+/)
      const shortWords = words.slice(0, 4)
      const extension = fullName.split('.').pop()

      if (shortWords.length >= 4 && words.length > 4) {
        return shortWords.join(' ') + '...' + (extension ? `.${extension}` : '')
      }
      return fullName
    },

    // 处理文件hover预览
    handleFileHover(file, event) {
      if (this.isImageFile(file.file_name)) {
        const rect = event.currentTarget.getBoundingClientRect()
        this.imagePreview = {
          visible: true,
          src: file.file_url,
          alt: file.display_name || file.original_name,
          x: rect.right + 10,
          y: rect.top
        }
      }
    },

    // 隐藏图片预览
    hideImagePreview() {
      this.imagePreview.visible = false
    },

    // 处理参考素材hover预览
    handleReferenceHover(reference, event) {
      if (this.isImageFile(reference.filename)) {
        const rect = event.currentTarget.getBoundingClientRect()
        this.imagePreview = {
          visible: true,
          src: reference.file_url,
          alt: reference.filename || '参考素材',
          x: rect.right + 10,
          y: rect.top
        }
      }
    },

    // 截断文本
    truncateText(text, maxLength) {
      if (!text) return ''
      if (text.length <= maxLength) return text
      return text.substring(0, maxLength) + '...'
    },

    clearAllFiles() {
      // 清空所有附件
      this.$confirm('确定要清空所有附件吗?', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      }).then(() => {
        this.uploadedFiles = []
        this.$message.success('附件已清空')
      }).catch(() => {})
    },

    refreshStorageStats() {
      this.storageStats = this.getStorageUsage()
    },

    cleanOldConversationsManually() {
      this.$confirm('确定要清理旧会话吗？这将删除最旧的会话，只保留最新的50个。', '清理确认', {
        confirmButtonText: '确定清理',
        cancelButtonText: '取消',
        type: 'warning'
      }).then(() => {
        this.autoCleanOldConversations()
        this.refreshStorageStats()
      })
    },

    clearAllChatData() {
      this.$confirm(
        '确定要清空所有聊天数据吗？这将删除所有历史会话，此操作不可恢复！',
        '危险操作',
        {
          confirmButtonText: '确定清空',
          cancelButtonText: '取消',
          type: 'error',
          inputPattern: /^CLEAR$/,
          inputPlaceholder: '请输入 CLEAR 确认',
          showInput: true,
          inputErrorMessage: '请输入 CLEAR'
        }
      ).then(() => {
        this.clearAllConversations()
      })
    },

    clearAllConversations() {
      try {
        // 清空当前会话
        this.chatMessages = []
        this.currentConversationId = null

        // 获取所有AI聊天相关的localStorage键
        const keysToRemove = []
        for (const key in localStorage) {
          if (key.startsWith(this.storageKeyPrefix)) {
            keysToRemove.push(key)
          }
        }

        // 删除所有相关数据
        keysToRemove.forEach(key => {
          localStorage.removeItem(key)
        })

        // 重置会话列表
        this.conversations = []
        this.historyPagination.total = 0

        // 刷新存储统计
        this.refreshStorageStats()

        this.$message.success(`已清空所有聊天数据，释放了存储空间`)
      } catch (error) {
        console.error('清空聊天数据失败:', error)
        this.$message.error('清空数据失败，请刷新页面重试')
      }
    },

    // 显示URL对话框
    showUrlDialog() {
      this.urlDialogVisible = true
      this.urlForm = {
        url: '',
        displayName: ''
      }
      this.$nextTick(() => {
        if (this.$refs.urlForm) {
          this.$refs.urlForm.clearValidate()
        }
      })
    },

    // 添加URL附件
    addUrlAttachment() {
      this.$refs.urlForm.validate((valid) => {
        if (valid) {
          this.urlAddingLoading = true

          try {
            // 生成唯一的uid
            const uid = Date.now() + Math.random().toString(36).substr(2, 9)

            // 从URL中提取文件名
            const fileName = this.urlForm.displayName || this.extractFileNameFromUrl(this.urlForm.url)

            // 检测文件类型
            const fileType = this.getFileTypeFromUrl(this.urlForm.url)

            // 创建URL附件对象
            const urlAttachment = {
              original_name: fileName,
              display_name: fileName,
              file_name: fileName,
              file_url: this.urlForm.url,
              file_type: fileType,
              uid: uid,
              size: 0, // URL附件大小设为0
              isUrl: true // 标记为URL附件
            }

            // 根据文件类型生成内容
            let fileContent = ''
            if (fileType === '图片') {
              fileContent = `![${fileName}](${this.urlForm.url})`
            } else {
              fileContent = `[${fileName}](${this.urlForm.url})`
            }
            urlAttachment.content = fileContent

            // 添加到文件列表
            urlAttachment.selected = false
            this.uploadedFiles.push(urlAttachment)

            this.$message.success(`已添加URL附件：${fileName}`)
            this.urlDialogVisible = false
          } catch (error) {
            console.error('添加URL附件失败:', error)
            this.$message.error('添加URL附件失败')
          } finally {
            this.urlAddingLoading = false
          }
        }
      })
    },

    // 处理URL对话框关闭
    handleCloseUrlDialog() {
      this.urlDialogVisible = false
      this.urlForm = {
        url: '',
        displayName: ''
      }
    },

    // 从URL中提取文件名
    extractFileNameFromUrl(url) {
      try {
        const urlObj = new URL(url)
        const pathname = urlObj.pathname
        const fileName = pathname.split('/').pop()

        if (fileName && fileName.includes('.')) {
          return fileName
        } else {
          // 如果没有文件名或扩展名，生成一个默认名称
          return `URL附件_${Date.now()}`
        }
      } catch (error) {
        return `URL附件_${Date.now()}`
      }
    },

    // 根据URL判断文件类型
    getFileTypeFromUrl(url) {
      try {
        const urlLower = url.toLowerCase()

        if (/\.(jpg|jpeg|png|gif|bmp|webp|svg)(\?|$)/.test(urlLower)) {
          return '图片'
        } else if (/\.(pdf)(\?|$)/.test(urlLower)) {
          return 'PDF'
        } else if (/\.(doc|docx)(\?|$)/.test(urlLower)) {
          return 'Word'
        } else if (/\.(xls|xlsx)(\?|$)/.test(urlLower)) {
          return 'Excel'
        } else if (/\.(ppt|pptx)(\?|$)/.test(urlLower)) {
          return 'PowerPoint'
        } else if (/\.(txt)(\?|$)/.test(urlLower)) {
          return '文本'
        } else if (/\.(zip|rar|7z)(\?|$)/.test(urlLower)) {
          return '压缩包'
        } else {
          return '其他'
        }
      } catch (error) {
        return '其他'
      }
    },

    // ========== AI默认参考素材相关方法 ==========
    // 加载节点的默认参考素材
    async loadNodeReferences(nodeId) {
      if (!nodeId) {
        this.nodeReferences = []
        this.selectedReferences = []
        return
      }

      try {
        const response = await getReferences(nodeId)
        if (response.success) {
          this.nodeReferences = response.data || []
          this.selectedReferences = [] // 默认不选择任何参考素材
        } else {
          console.error('加载节点参考素材失败:', response.msg)
          this.nodeReferences = []
          this.selectedReferences = []
        }
      } catch (error) {
        console.error('加载节点参考素材失败:', error)
        this.nodeReferences = []
        this.selectedReferences = []
      }
    },

    // 切换默认参考素材的选择状态（单选模式）
    toggleReferenceSelection(reference) {
      const index = this.selectedReferences.findIndex(ref => ref.id === reference.id)
      if (index > -1) {
        // 如果已选中，则取消选择
        this.selectedReferences.splice(index, 1)
      } else {
        // 如果未选中，则先清空其他选择，再选中当前的（单选模式）
        this.selectedReferences = [reference]
      }
    },

    // 判断默认参考素材是否被选中
    isReferenceSelected(reference) {
      return this.selectedReferences.some(ref => ref.id === reference.id)
    },

    // 清空选中的默认参考素材
    clearSelectedReferences() {
      this.selectedReferences = []
    },

    // 获取参考素材的文件类型
    getFileTypeFromReference(reference) {
      if (reference.file_type) {
        return reference.file_type
      }
      // 根据文件名扩展名判断
      if (reference.filename) {
        const ext = reference.filename.toLowerCase().split('.').pop()
        if (['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp'].includes(ext)) {
          return 'image'
        } else if (['pdf'].includes(ext)) {
          return 'document'
        } else if (['doc', 'docx'].includes(ext)) {
          return 'document'
        } else if (['xls', 'xlsx'].includes(ext)) {
          return 'document'
        }
      }
      return 'file'
    },

    // 文件名截断
    truncateFileName(filename, maxLength = 15) {
      if (!filename || filename.length <= maxLength) return filename
      const ext = filename.split('.').pop()
      const name = filename.substring(0, filename.lastIndexOf('.'))
      const truncated = name.substring(0, maxLength - ext.length - 3) + '...'
      return truncated + '.' + ext
    },

    // 打开参考素材选择对话框
    openReferenceDialog() {
      this.referenceDialogVisible = true
    },

    // 全选参考素材
    selectAllReferences() {
      this.selectedReferences = [...this.nodeReferences]
    },

    // 确认参考素材选择
    confirmReferenceSelection() {
      this.referenceDialogVisible = false
      if (this.selectedReferences.length > 0) {
        this.$message.success('已选择参考素材')
      } else {
        this.$message.info('未选择参考素材')
      }
    },

    // 判断是否为PDF文件
    isPDFFile(filename) {
      return filename && filename.toLowerCase().endsWith('.pdf')
    }
  }
}
</script>

  <style scoped>
  .app-container {
    background: rgba(246, 246, 246, 0.9);
    height: calc(100vh - 84px);
    overflow: hidden;
    padding: 20px;
  }

  /* 左右分栏布局样式 */
  .main-container {
    height: 100%;
  }

  .left-panel {
    height: 100%;
  }

  .right-panel {
    height: 100%;
  }

  .chat-panel {
    height: 100%;
    background: #fff;
    border-radius: 4px;
    box-shadow: 0 1px 4px rgba(0, 0, 0, 0.1);
    display: flex;
    flex-direction: column;
    padding: 16px;
  }

  /* 模型选择器样式 */
  .model-selector-container {
    margin-bottom: 16px;
    border: 1px solid #e6e8ff;
    border-radius: 6px;
    background: #f8f9ff;
  }

  .model-selector-header {
    padding: 0;
  }

  .selected-model-display {
    padding: 12px 16px;
    display: flex;
    align-items: center;
    justify-content: space-between;
    border-bottom: 1px solid #e6e8ff;
  }

  .model-selector-trigger:hover {
    border-color: #1EB7E6;
    background: #f0f8ff;
  }

  .selected-model,
  .placeholder-text {
    display: flex;
    align-items: center;
    flex: 1;
  }

  .selected-model i,
  .placeholder-text i {
    color: #1EB7E6;
    margin-right: 8px;
  }

  .model-name {
    font-weight: 500;
    color: #333;
    margin-right: 8px;
  }

  .placeholder-text {
    color: #909399;
  }

  .tree-toggle-btn {
    padding: 4px 8px !important;
    color: #1EB7E6 !important;
  }

  .tree-selector-content {
    padding: 10px;
  }

  .tree-header {
    display: flex;
    align-items: center;
    margin-bottom: 10px;
  }

  .chat-input-section {
    display: flex;
    flex-direction: column;
    flex: 1;
    min-height: 0;
  }

  /* 图片生成参数选择器样式 - 紧凑版 */
  .image-params-section {
    background: #f5f7fa;
    border: 1px solid #e4e7ed;
    border-radius: 4px;
    padding: 6px 10px;
    margin-bottom: 8px;
  }

  .image-params-section .params-controls {
    display: flex;
    gap: 8px;
    align-items: center;
  }

  .image-params-section .param-item {
    display: flex;
    align-items: center;
    gap: 6px;
    flex: 1;
  }

  .image-params-section .param-item label {
    font-size: 11px;
    color: #606266;
    white-space: nowrap;
  }

  .image-params-section .param-item .el-select {
    flex: 1;
  }

  /* 减小选择器内文字大小 */
  .image-params-section >>> .el-input__inner {
    font-size: 11px;
    height: 26px;
    line-height: 26px;
  }

  .image-params-section >>> .el-input__icon {
    line-height: 26px;
  }

  /* 主要内容区域（可滚动） */
  .chat-content-area {
    flex: 1;
    overflow-y: auto;
    min-height: 0;
    margin-bottom: 16px;
  }

  /* 输入区域（固定在底部） */
  .chat-input-area {
    flex-shrink: 0;
    background: #fff;
    border-top: 1px solid #e4e7ed;
    padding-top: 16px;
    display: flex;
    flex-direction: column;
  }

  /* 当前模型信息样式 */
  .current-model-info {
    margin-bottom: 16px;
    padding: 12px 16px;
    background: #f8f9ff;
    border: 1px solid #e6e8ff;
    border-radius: 6px;
  }

  .model-info-card {
    display: flex;
    align-items: center;
    gap: 8px;
  }

  .model-info-card .el-icon-cpu {
    color: #1EB7E6;
    font-size: 16px;
  }

  .model-name {
    font-weight: 500;
    color: #333;
    flex: 1;
  }

  /* 主Tab布局样式 */
  .main-tabs {
    height: 100%;
    background: #fff;
    border-radius: 4px;
    box-shadow: 0 1px 4px rgba(0, 0, 0, 0.1);
  }

  .main-tabs .el-tabs__content {
    height: calc(100% - 40px);
    overflow: hidden;
  }

  .main-tabs .el-tab-pane {
    height: 100%;
    overflow: hidden;
  }

  /* 选择模型Tab样式 */
  .model-selection-container {
    height: 100%;
    display: flex;
    flex-direction: column;
    padding: 20px;
    height: 100%;
    display: flex;
    flex-direction: column;
    padding: 16px;
  }

  .search-container {
    margin-bottom: 16px;
    display: flex;
    align-items: center;
  }

  .search-container .el-input {
    flex: 1;
    margin-right: 10px;
  }

  .refresh-btn {
    flex-shrink: 0;
  }

  .model-tree {
    flex: 1;
    overflow: auto;
  }

  /* 交互命令Tab样式 */
  .chat-interface {
    height: 100%;
    display: flex;
    flex-direction: column;
  }

  .chat-container {
    height: 100%;
    display: flex;
    flex-direction: column;
  }

  .chat-header {
    padding: 4px 0;
    background: rgba(255, 255, 255, 0.7);
    backdrop-filter: blur(10px);
    -webkit-backdrop-filter: blur(10px);
    border-bottom: 1px solid rgba(229, 229, 229, 0.3);
    position: sticky;
    top: 0;
    z-index: 100;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
  }

  .chat-header .model-name {
    font-size: 13px;
    font-weight: 500;
    color: #606266;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    flex: 1;
    line-height: 24px;
    opacity: 0.9;
  }

  .conversation-controls {
    display: flex;
    align-items: center;
    width: 100%;
  }

  .header-content {
    display: flex;
    justify-content: space-between;
    align-items: center;
    width: 100%;
    gap: 10px;
    padding: 0 12px;
  }

  .conversation-actions {
    display: flex;
    gap: 2px;
    justify-content: flex-end;
    flex-shrink: 0;
  }

  .conversation-actions .el-button--mini.is-circle {
    width: 24px;
    height: 24px;
    padding: 0;
    font-size: 11px;
    border: none;
    background: rgba(0, 0, 0, 0.03);
    color: #606266;
    transition: all 0.2s ease;
  }

  .conversation-actions .el-button--mini.is-circle:hover {
    background: rgba(64, 158, 255, 0.15);
    color: #409EFF;
    transform: scale(1.05);
  }

  .conversation-actions .el-button--mini.is-circle:disabled {
    background: transparent;
    color: #C0C4CC;
    opacity: 0.4;
  }

  .model-info {
    text-align: center;
    font-size: 14px;
    margin-bottom: 10px;
  }

  .model-placeholder {
    text-align: left;
    font-size: 14px;
    margin: 10px 0;
    padding: 0 10px;
  }

  .placeholder-text {
    margin: 5px 0;
    padding: 8px;
    background-color: #f5f7fa;
    border-radius: 4px;
    font-size: 13px;
    line-height: 1.5;
    color: #606266;
  }

  .chat-messages {
    flex: 1;
    padding: 10px 20px;
    overflow-y: auto;
    display: flex;
    flex-direction: column;
    margin-top: -1px; /* 去除顶部的间隙 */
  }

  .message {
    display: flex;
    margin-bottom: 20px;
    width: 100%;
  }

  .user {
    justify-content: flex-start;
  }

  .assistant {
    justify-content: flex-end;
  }

  .system {
    justify-content: center;
  }

  .system .message-content {
    background-color: #f4f4f5;
    color: #909399;
    font-size: 12px;
    padding: 8px 12px;
    border-radius: 4px;
    text-align: center;
    max-width: 80%;
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  }

  .message-avatar {
    width: 40px;
    height: 40px;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    margin-right: 12px;
    flex-shrink: 0;
  }

  .assistant .message-avatar {
    margin-right: 0;
    margin-left: 12px;
  }

  .user .message-avatar {
    background-color: #ecf5ff;
    color: #409EFF;
  }

  .assistant .message-avatar {
    background-color: #f0f9eb;
    color: #67C23A;
  }

  .message-body {
    flex: 0 1 auto;
    max-width: 95%;
    width: calc(100% - 60px);  /* 减去头像和间距的宽度 */
    position: relative; /* 为复制按钮定位做准备 */
  }

  .system .message-body {
    width: auto;
    max-width: 80%;
  }

  .message-info {
    margin-bottom: 4px;
    display: flex;
    justify-content: space-between;
  }

  .user .message-info {
    flex-direction: row;
  }

  .assistant .message-info {
    flex-direction: row-reverse;
  }

  .message-sender {
    font-weight: 500;
    font-size: 14px;
  }

  .message-time {
    color: #909399;
    font-size: 12px;
  }

  .message-content {
    padding: 12px;
    border-radius: 8px;
    font-size: 14px;
    line-height: 1.6;
    word-break: break-word;
    position: relative; /* 为复制按钮定位做准备 */
    padding-bottom: 30px; /* 为复制按钮留出空间 */
  }

  .message-content a {
    color: #409EFF !important;
    text-decoration: underline !important;
  }

  .user .message-content {
    background-color: #ecf5ff;
    border-top-left-radius: 0;
    white-space: pre-wrap;
  }

  .assistant .message-content {
    background-color: #f5f7fa;
    border-top-right-radius: 0;
  }

  .message-actions {
    position: absolute;
    bottom: 5px;
    right: 5px;
    opacity: 0;
    transition: opacity 0.2s ease;
    z-index: 2;
    display: flex;  /* 添加flex布局 */
    flex-direction: row; /* 水平排列 */
    gap: 5px; /* 按钮之间的间距 */
  }

  .message-body:hover .message-actions {
    opacity: 1;
  }

  .copy-btn {
    padding: 2px 5px;
    color: #909399;
  }

  .copy-btn:hover {
    color: #409EFF;
  }

  /* 覆盖原生按钮样式 */
  .copy-btn.el-button--text {
    background: rgba(255, 255, 255, 0.8);
    border-radius: 4px;
    font-size: 12px;
    padding: 2px 8px;
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
    display: flex;
    align-items: center;
  }

  .copy-btn i {
    margin-right: 3px;
  }

  .translate-btn {
    padding: 2px 5px;
    color: #409EFF;
    margin-left: 0; /* 移除左边距，使用gap控制间距 */
  }

  .translate-btn.el-button--text {
    background: rgba(255, 255, 255, 0.8);
    border-radius: 4px;
    font-size: 12px;
    padding: 2px 8px;
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
    display: flex;
    align-items: center;
  }

  .translate-btn.el-button--text[disabled] {
    color: #a0cfff;
    background: rgba(255, 255, 255, 0.9);
    cursor: not-allowed;
  }

  .translate-btn i {
    margin-right: 3px;
  }

  .empty-chat {
    display: flex;
    justify-content: center;
    align-items: center;
    flex: 1;
  }

  .empty-chat-content {
    text-align: center;
    color: #909399;
  }

  .empty-icon {
    font-size: 48px;
    margin-bottom: 16px;
  }

  .loading-message {
    align-self: flex-start;
    color: #909399;
    margin: 10px 0;
    font-size: 14px;
    display: flex;
    align-items: center;
    justify-content: space-between;
    width: 100%;
    background-color: #f5f7fa;
    padding: 8px 12px;
    border-radius: 4px;
    margin-bottom: 10px;
  }

  .loading-message span {
    display: flex;
    align-items: center;
  }

  .message-count-info {
    margin-bottom: 10px;
    font-size: 12px;
    color: #606266;
  }

  .message-count-progress {
    margin-top: 5px;
  }

  .cancel-btn {
    padding: 5px 10px;
  }

  .cancel-btn .svg-icon {
    width: 16px;
    height: 16px;
  }

  /* 右侧AI思考提示样式 */
  .loading-message-right {
    display: flex;
    justify-content: flex-end;
    padding: 15px 20px;
    margin: 10px 0;
  }

  .loading-message-right .loading-content {
    display: flex;
    align-items: center;
    gap: 10px;
    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
    color: white;
    padding: 12px 20px;
    border-radius: 20px;
    font-size: 14px;
    box-shadow: 0 2px 12px rgba(102, 126, 234, 0.3);
  }

  .loading-message-right .loading-content i {
    font-size: 16px;
    animation: rotate 1s linear infinite;
  }

  .loading-message-right .loading-content span {
    font-weight: 500;
  }

  .loading-message-right .loading-content .cancel-btn {
    margin-left: 10px;
    padding: 5px 10px;
  }

  @keyframes rotate {
    from {
      transform: rotate(0deg);
    }
    to {
      transform: rotate(360deg);
    }
  }

  .chat-input {
    padding: 0;
  }

  .input-actions {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-top: 8px;
  }

  .left-actions {
    display: flex;
    align-items: center;
  }

  .right-actions {
    display: flex;
    align-items: center;
    gap: 10px;
  }

  .upload-button {
    margin-right: 5px;
  }

  .upload-button :deep(.el-button) {
    padding: 5px 10px;
    display: flex;
    align-items: center;
    color: #409EFF;
  }

  .upload-button :deep(.el-icon-upload2) {
    margin-right: 3px;
    font-size: 14px;
  }

  .upload-tip {
    font-size: 12px;
    color: #909399;
    margin-left: 5px;
  }

  .input-tip {
    color: #909399;
    font-size: 12px;
    margin-right: 10px;
  }

  /* 覆盖markdown样式 */
  :deep(.message-content) {
    overflow: auto;
    line-height: 1.5; /* 减小行高 */
  }

  :deep(.message-content pre) {
    background-color: #f8f8f8;
    padding: 10px;
    border-radius: 4px;
    overflow-x: auto;
    margin: 6px 0;
  }

  :deep(.message-content code) {
    font-family: Consolas, Monaco, 'Andale Mono', monospace;
  }

  :deep(.message-content p) {
    margin: 4px 0; /* 减少段落间距 */
  }

  :deep(.message-content h1, .message-content h2, .message-content h3) {
    margin-top: 16px;
    margin-bottom: 8px;
  }

  :deep(.message-content h1) {
    margin: 8px 0 4px 0; /* 减少标题上下边距 */
    font-size: 1.4em;
  }

  :deep(.message-content h2) {
    margin: 6px 0 4px 0;
    font-size: 1.3em;
  }

  :deep(.message-content h3) {
    margin: 5px 0 3px 0;
    font-size: 1.2em;
  }

  .message-content img {
    max-width: 100%;
    height: auto;
    border-radius: 4px;
    margin: 8px 0;
    display: block;
  }

  :deep(.message-content img) {
    max-width: 100%;
    height: auto;
    border-radius: 4px;
    margin: 8px 0;
    display: block;
  }

  :deep(.message-content a) {
    color: #409EFF !important;
    text-decoration: underline !important;
    border-bottom: 1px solid #d0e2ff !important;
    padding-bottom: 1px !important;
    transition: all 0.2s ease !important;
    font-weight: 500 !important;
  }

  :deep(.message-content a:hover) {
    border-bottom-color: #409EFF !important;
    background-color: rgba(64, 158, 255, 0.1) !important;
    text-decoration: underline !important;
  }

  :deep(.message-content a:visited) {
    color: #8c6dff !important;
  }

  :deep(.message-content a:active) {
    color: #f56c6c !important;
  }

  /* Markdown表格样式 */
  :deep(.message-content table) {
    width: 100%;
    border-collapse: collapse;
    margin: 15px 0;
    border: 2px solid #dcdfe6;
    border-radius: 6px;
    box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
    overflow: hidden;
    font-size: 14px;
  }

  :deep(.message-content thead) {
    background-color: #409EFF;
  }

  :deep(.message-content th) {
    background-color: #409EFF;
    color: white;
    font-weight: bold;
    padding: 12px 15px;
    border: 1px solid #4ba3ff;
    text-align: left;
    letter-spacing: 0.5px;
  }

  :deep(.message-content td) {
    padding: 12px 15px;
    border: 1px solid #ebeef5;
    background-color: #fff;
    line-height: 1.5;
  }

  :deep(.message-content tr:nth-child(even) td) {
    background-color: #f9fafc;
  }

  :deep(.message-content tr:hover td) {
    background-color: #ecf5ff;
    transition: background-color 0.3s ease;
  }

  :deep(.message-content table caption) {
    caption-side: bottom;
    padding: 10px;
    font-style: italic;
    color: #909399;
    text-align: center;
  }

  /* HTML预览样式 */
  :deep(.html-preview) {
    border: 2px solid #dcdfe6;
    border-radius: 6px;
    margin: 10px 0;
    overflow: hidden;
    box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
  }

  :deep(.html-preview-header) {
    background-color: #409EFF;
    color: white;
    padding: 8px 12px;
    font-weight: bold;
    font-size: 14px;
    display: flex;
    justify-content: space-between;
    align-items: center;
  }

  :deep(.html-preview-actions) {
    display: flex;
    gap: 5px;
  }

  :deep(.html-preview-actions button) {
    background: rgba(255, 255, 255, 0.2);
    border: none;
    border-radius: 4px;
    color: white;
    padding: 3px 8px;
    font-size: 12px;
    cursor: pointer;
    display: flex;
    align-items: center;
    gap: 4px;
    transition: background 0.3s;
  }

  :deep(.html-preview-actions button:hover) {
    background: rgba(255, 255, 255, 0.4);
  }

  :deep(.html-iframe) {
    width: 100%;
    min-height: 300px;
    background-color: white;
    display: block;
    border: none;
    transition: all 0.3s ease;
  }

  :deep(.html-iframe.fullscreen) {
    height: 80vh !important;
    box-shadow: 0 0 20px rgba(0, 0, 0, 0.5);
    border-radius: 8px;
    background-color: white;
    max-width: 90vw;
    margin: 0 auto;
  }

  /* 添加遮罩层样式 */
  :deep(.iframe-overlay) {
    position: fixed;
    top: 0;
    left: 0;
    width: 100vw;
    height: 100vh;
    background-color: rgba(0, 0, 0, 0.7);
    z-index: 99998;
    display: flex;
    justify-content: center;
    align-items: center;
  }

  /* 关闭按钮样式 */
  :deep(.iframe-close-btn) {
    position: fixed;
    top: 5%;
    right: 10%;
    background-color: #f56c6c;
    color: white;
    border: none;
    border-radius: 4px;
    padding: 10px 20px;
    font-size: 16px;
    font-weight: bold;
    cursor: pointer;
    z-index: 100000;
    display: flex;
    align-items: center;
    gap: 8px;
    box-shadow: 0 4px 12px 0 rgba(0, 0, 0, 0.3);
    transition: all 0.3s;
    animation: pulse 2s infinite;
  }

  @keyframes pulse {
    0% {
      box-shadow: 0 0 0 0 rgba(245, 108, 108, 0.7);
    }
    70% {
      box-shadow: 0 0 0 10px rgba(245, 108, 108, 0);
    }
    100% {
      box-shadow: 0 0 0 0 rgba(245, 108, 108, 0);
    }
  }

  :deep(.iframe-close-btn:hover) {
    background-color: #f78989;
    transform: scale(1.05);
    box-shadow: 0 6px 16px 0 rgba(0, 0, 0, 0.4);
  }

  :deep(.iframe-close-btn i) {
    font-size: 18px;
  }

  /* 全屏提示信息样式 */
  :deep(.iframe-fullscreen-tip) {
    position: fixed;
    bottom: 5%;
    left: 50%;
    transform: translateX(-50%);
    background-color: rgba(0, 0, 0, 0.7);
    color: white;
    padding: 10px 20px;
    border-radius: 4px;
    font-size: 14px;
    z-index: 100000;
    text-align: center;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
    animation: fadeOut 0.5s 5s forwards;
  }

  @keyframes fadeOut {
    from { opacity: 1; }
    to { opacity: 0; visibility: hidden; }
  }
  /* 自定义树形结构样式 */
  .model-tree .el-tree-node__content {
    height: 30px; /* 进一步减小节点高度 */
  }

  .model-tree .el-tree-node__children {
    padding-left: 12px !important; /* 减小层级缩进 */
  }

  .model-tree .el-tree-node__expand-icon {
    padding: 5px; /* 调整展开图标的大小 */
  }

  .model-tree .el-tree-node__label {
    font-size: 13px; /* 进一步减小字体大小 */
  }

  /* 增强树形节点选中状态的视觉效果 - 使用更醒目的深色 */
  .model-tree ::v-deep .el-tree-node.is-current > .el-tree-node__content {
    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important;
    color: white !important;
    border-radius: 6px;
    position: relative;
    box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4);
    font-weight: 600;
    transition: all 0.3s ease;
  }

  .model-tree ::v-deep .el-tree-node.is-current > .el-tree-node__content:hover {
    background: linear-gradient(135deg, #5a67d8 0%, #6b3b94 100%) !important;
    color: white !important;
    box-shadow: 0 6px 16px rgba(102, 126, 234, 0.5);
  }

  .model-tree ::v-deep .el-tree-node.is-current > .el-tree-node__content .el-tree-node__label {
    color: white !important;
    font-weight: 700;
    text-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
  }

  .model-tree ::v-deep .el-tree-node.is-current > .el-tree-node__content .el-tree-node__expand-icon {
    color: white !important;
  }

  /* 为选中节点添加左侧亮色指示条 */
  .model-tree ::v-deep .el-tree-node.is-current > .el-tree-node__content::before {
    content: '';
    position: absolute;
    left: 0;
    top: 3px;
    bottom: 3px;
    width: 5px;
    background: #ffd700;
    border-radius: 0 3px 3px 0;
    box-shadow: 0 0 8px rgba(255, 215, 0, 0.6);
  }

  /* 添加选中节点的动画效果已合并到主样式中 */

  .conversation-actions .el-select {
    width: 180px;
  }

  .history-drawer-content {
    padding: 20px;
    height: 100%;
    display: flex;
    flex-direction: column;
  }

  .history-search {
    margin-bottom: 15px;
  }

  .history-actions {
    display: flex;
    justify-content: space-between;
    margin-bottom: 15px;
  }

  .history-pagination {
    margin-top: 15px;
    display: flex;
    justify-content: center;
  }

  .empty-history {
    text-align: center;
    color: #909399;
    margin-top: 50px;
  }

  .empty-history .empty-icon {
    font-size: 48px;
    margin-bottom: 16px;
  }

  .history-title-container {
    width: 100%;
  }

  .history-title {
    font-weight: 500;
    color: #303133;
    margin-bottom: 5px;
    cursor: pointer;
    display: flex;
    align-items: flex-start; /* 改为顶部对齐 */
    padding: 3px 0;
    line-height: 1.4; /* 增加行高 */
    max-width: 100%; /* 确保不会溢出 */
    word-break: break-word; /* 允许单词内断行 */
  }

  .history-title:hover {
    background-color: #f5f7fa;
    border-radius: 3px;
  }

  .edit-icon {
    opacity: 0;
    margin-left: 5px;
    color: #409EFF;
    transition: opacity 0.3s;
    flex-shrink: 0; /* 防止图标被压缩 */
  }

  .history-title:hover .edit-icon {
    opacity: 1;
  }

  .history-time {
    font-size: 12px;
    color: #909399;
  }

  .model-info {
    text-align: center;
    font-size: 14px;
    margin-bottom: 10px;
  }

  .settings-content {
    padding: 10px;
  }

  .setting-tip {
    font-size: 12px;
    color: #909399;
    margin-top: 5px;
    line-height: 1.4;
  }

  .performance-tips {
    background-color: #f8f8f8;
    padding: 10px 15px;
    border-radius: 4px;
    font-size: 13px;
    color: #606266;
    margin-top: 10px;
  }

  .performance-tips p {
    margin: 5px 0;
  }

  .performance-tips ul {
    margin: 8px 0;
    padding-left: 20px;
  }

  .performance-tips li {
    margin-bottom: 5px;
  }
  /* 统一设置所有标题的边距 */
  :deep(.message-content h1),
  :deep(.message-content h2),
  :deep(.message-content h3),
  :deep(.message-content h4),
  :deep(.message-content h5),
  :deep(.message-content h6) {
    margin: 6px 0 4px 0 !important; /* 显著减少上下边距 */
    padding: 0 !important; /* 移除内边距 */
    line-height: 1.3 !important; /* 减小行高 */
  }

  /* 根据标题级别设置字体大小 */
  :deep(.message-content h1) {
    font-size: 1.5em !important;
  }

  :deep(.message-content h2) {
    font-size: 1.3em !important;
  }

  :deep(.message-content h3) {
    font-size: 1.2em !important;
  }

  :deep(.message-content h4) {
    font-size: 1.1em !important;
  }

  :deep(.message-content h5),
  :deep(.message-content h6) {
    font-size: 1em !important;
  }
  /* 优化段落和列表间距 */
  :deep(.message-content p) {
    margin: 3px 0 !important; /* 减少段落间距 */
  }

  :deep(.message-content ul),
  :deep(.message-content ol) {
    margin: 3px 0 !important; /* 减少列表上下边距 */
    padding-left: 20px !important; /* 保持列表缩进 */
  }

  :deep(.message-content li) {
    margin-bottom: 2px !important; /* 减少列表项间距 */
  }

  /* 优化代码块和行内代码样式 */
  :deep(.message-content pre) {
    margin: 4px 0 !important; /* 减少代码块间距 */
    padding: 8px !important; /* 减少内边距 */
  }

  :deep(.message-content code) {
    background-color: rgba(0, 0, 0, 0.05) !important; /* 添加淡色背景以区分行内代码 */
    padding: 0 3px !important;
    border-radius: 3px !important;
    font-family: Consolas, Monaco, 'Andale Mono', monospace !important;
    font-size: 0.9em !important;
  }

  /* 优化内容整体样式 */
  :deep(.message-content) {
    line-height: 1.4 !important; /* 减小行高，使内容更紧凑 */
    font-size: 14px !important; /* 确保字体大小一致 */
  }

  /* 优化引用块样式 */
  :deep(.message-content blockquote) {
    margin: 4px 0 4px 0 !important;
    padding: 4px 8px !important;
    border-left: 3px solid #409EFF !important;
    background-color: rgba(64, 158, 255, 0.05) !important;
    color: #606266 !important;
  }

  /* 优化图片样式，确保自适应显示 */
  :deep(.message-content img) {
    max-width: 100% !important;
    height: auto !important;
    object-fit: contain !important;
    display: block !important;
    margin: 8px auto !important;
    border-radius: 4px !important;
  }

  /* HTML代码块样式 */
  :deep(.code-block-container) {
    margin: 10px 0 !important;
    border-radius: 6px !important;
    overflow: hidden !important;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15) !important;
  }

  :deep(.code-block-header) {
    background-color: #1e1e1e !important;
    color: white !important;
    padding: 8px 12px !important;
    display: flex !important;
    justify-content: space-between !important;
    align-items: center !important;
    font-size: 14px !important;
    font-family: Monaco, Consolas, 'Courier New', monospace !important;
  }

  :deep(.code-block-actions) {
    display: flex !important;
    gap: 8px !important;
  }

  :deep(.code-block-actions button) {
    background: rgba(255, 255, 255, 0.1) !important;
    border: none !important;
    border-radius: 4px !important;
    color: white !important;
    padding: 4px 8px !important;
    font-size: 12px !important;
    cursor: pointer !important;
    display: flex !important;
    align-items: center !important;
    gap: 4px !important;
    transition: background 0.2s !important;
  }

  :deep(.code-block-actions button:hover) {
    background: rgba(255, 255, 255, 0.2) !important;
  }

  :deep(.code-block) {
    margin: 0 !important;
    padding: 12px !important;
    background-color: #1e1e1e !important;
    color: #d4d4d4 !important;
    overflow-x: auto !important;
    font-family: Monaco, Consolas, 'Courier New', monospace !important;
    font-size: 14px !important;
    line-height: 1.5 !important;
    max-height: 500px !important;
    overflow-y: auto !important;
  }

  :deep(.code-block code) {
    background: transparent !important;
    padding: 0 !important;
    border-radius: 0 !important;
    color: inherit !important;
    font-family: inherit !important;
    white-space: pre-wrap !important;
    word-break: break-all !important;
  }
  </style>

  <style scoped>
  /* 上传文件列表样式 */
  .uploaded-files-container {
    margin-top: 10px;
    border: 1px solid #EBEEF5;
    border-radius: 4px;
    background-color: #fafafa;
  }

  .uploaded-files-header {
    padding: 8px 12px;
    border-bottom: 1px solid #EBEEF5;
    font-size: 14px;
    color: #606266;
    background-color: #f5f7fa;
  }

  .uploaded-files-list {
    padding: 8px 12px;
    max-height: 120px;
    overflow-y: auto;
  }

  .uploaded-file-item {
    display: flex;
    align-items: center;
    padding: 5px 0;
    border-bottom: 1px solid #f0f0f0;
    flex-wrap: nowrap;
    width: 100%;
  }

  .uploaded-file-item:last-child {
    border-bottom: none;
  }

  /* 消息中的附件显示样式 */
  .message-files-container {
    margin-top: 8px;
    border: 1px solid #EBEEF5;
    border-radius: 4px;
    background-color: #f9f9f9;
  }

  .message-files-list {
    padding: 6px 10px;
    max-height: 100px;
    overflow-y: auto;
  }

  .message-file-item {
    display: flex;
    align-items: center;
    padding: 4px 0;
    border-bottom: 1px solid #f0f0f0;
    flex-wrap: nowrap;
    width: 100%;
  }

  .message-file-item:last-child {
    border-bottom: none;
  }

  .file-thumbnail {
    width: 60px;
    height: 60px;
    margin-right: 10px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 4px;
    overflow: hidden;
    background-color: #f5f7fa;
    cursor: pointer;
    transition: all 0.2s ease;
  }

  .file-thumbnail:hover {
    transform: scale(1.05);
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
  }

  .thumbnail-image {
    width: 100%;
    height: 100%;
    object-fit: cover;
    border-radius: 4px;
  }

  /* 网格布局的上传文件列表 */
  .uploaded-files-grid {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
    margin-bottom: 10px;
    padding: 10px;
    background-color: #f9f9f9;
    border-radius: 6px;
    border: 1px solid #e4e7ed;
  }

  .file-grid-item {
    display: flex;
    flex-direction: column;
    align-items: center;
    position: relative;
    width: 120px;
    padding: 8px 8px 4px 8px;
    background: white;
    border-radius: 6px;
    border: 1px solid #e4e7ed;
    transition: all 0.2s ease;
  }

  .file-grid-item:hover {
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
    border-color: #409eff;
  }

  .file-preview {
    width: 100px;
    height: 80px;
    margin-bottom: 5px;
    border-radius: 4px;
    overflow: hidden;
    display: flex;
    align-items: center;
    justify-content: center;
  }

  .image-thumbnail {
    width: 100%;
    height: 100%;
    cursor: pointer;
  }

  .image-thumbnail img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    border-radius: 4px;
  }

  .file-icon-large {
    width: 100%;
    height: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
    background-color: #f5f7fa;
    border-radius: 4px;
  }

  .file-icon-large i {
    font-size: 32px;
    color: #909399;
  }

  .file-actions-hover {
    position: absolute;
    bottom: 4px;
    left: 50%;
    transform: translateX(-50%);
    display: flex;
    gap: 6px;
    justify-content: center;
    opacity: 0;
    transition: opacity 0.3s ease;
    pointer-events: none;
  }

  .file-grid-item:hover .file-actions-hover {
    opacity: 1;
    pointer-events: auto;
  }

  .file-actions-hover i {
    font-size: 12px;
    color: #909399;
    cursor: pointer;
    padding: 3px;
    border-radius: 50%;
    transition: all 0.2s ease;
    background: rgba(255, 255, 255, 0.95);
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
  }

  .file-actions-hover i:hover {
    color: #409eff;
    background-color: #ecf5ff;
    transform: scale(1.1);
  }

  .file-actions-hover .el-icon-delete:hover {
    color: #f56c6c;
    background-color: #fef0f0;
  }

  .file-icon {
    width: 28px;
    height: 28px;
    margin-right: 10px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 4px;
    flex-shrink: 0;
  }

  .file-icon i {
    font-size: 18px;
  }

  .file-icon.图片 {
    background-color: #ecf5ff;
    color: #409EFF;
  }

  .file-icon.excel {
    background-color: #f0f9eb;
    color: #67C23A;
  }

  .file-icon.文本 {
    background-color: #fdf6ec;
    color: #E6A23C;
  }

  .file-info {
    flex: 1;
    min-width: 0; /* 确保文本可以正确截断 */
    overflow: hidden;
    display: flex;
    align-items: center;
  }

  .file-name {
    font-size: 14px;
    color: #606266;
    white-space: nowrap;
    text-overflow: ellipsis;
    overflow: hidden;
    width: 100%;
  }

  .file-actions {
    margin-left: 10px;
    white-space: nowrap;
    display: flex;
    flex-shrink: 0;
  }

  .file-actions .el-button {
    padding: 2px 5px;
    font-size: 12px;
  }

  .file-actions .el-button + .el-button {
    margin-left: 5px;
  }

  /* 图片预览对话框样式 */
  :deep(.image-preview-dialog) {
    width: auto !important;
    max-width: 90% !important;
  }

  /* 图片预览容器 */
  .image-preview-container {
    display: flex;
    justify-content: center;
    align-items: center;
    min-height: 200px;
  }

  /* 移除app-container的边距为了节约空间 */
  .app-container.no-margin {
    padding: 0 !important;
    margin: 0 !important;
  }

  /* markdown中的图片点击效果 */
  :deep(.markdown-image-clickable) {
    transition: opacity 0.2s ease, transform 0.2s ease;
    cursor: pointer;
    position: relative;
  }

  :deep(.markdown-image-clickable:hover) {
    opacity: 0.95;
    transform: scale(1.01);
  }

  /* 添加响应式图片容器样式 */
  :deep(.message-content .image-container) {
    display: flex;
    justify-content: center;
    align-items: center;
    width: 100%;
    overflow: hidden;
  }

  /* 小屏幕优化 */
  @media (max-width: 768px) {
    :deep(.message-content img) {
      max-height: 40vh !important; /* 在小屏幕上减小最大高度 */
    }

    .chat-messages {
      padding: 10px !important; /* 减少小屏幕上的内边距 */
    }
  }

  /* 大屏幕优化 */
  @media (min-width: 1920px) {
    :deep(.message-content img) {
      max-height: 70vh !important; /* 在大屏幕上增加最大高度 */
      max-width: 90% !important; /* 稍微限制宽度，避免过大 */
    }
  }

  /* 图片预览对话框位置调整 */
  :deep(.el-dialog__wrapper) {
    overflow-y: auto;
  }

  :deep(.el-dialog) {
    margin-top: 20px !important;
    margin-bottom: 20px !important;
  }

  .input-actions {
    margin-top: 12px;
  }

  .action-row {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 8px;
    flex-wrap: wrap;
    gap: 8px;
  }

  .left-actions {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
  }

  .right-actions {
    display: flex;
    align-items: center;
    gap: 8px;
  }

  .send-row {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 10px;
  }

  .input-tip {
    font-size: 12px;
    color: #909399;
    flex-shrink: 0;
  }

  /* 响应式设计 */
  @media (max-height: 800px) {
    .chat-content-area {
      margin-bottom: 12px;
    }

    .chat-input-area {
      padding-top: 12px;
    }

    .input-actions {
      margin-top: 8px;
    }

    .action-row {
      margin-bottom: 6px;
    }
  }

  @media (max-height: 600px) {
    .model-selector-container {
      margin-bottom: 12px;
    }

    .chat-content-area {
      margin-bottom: 8px;
    }

    .chat-input-area {
      padding-top: 8px;
    }

    .node-references-section {
      margin-bottom: 8px;
    }

    .uploaded-files-container {
      margin-top: 8px;
    }
  }

  /* 内容高度自适应 */
  .node-references-grid {
    max-height: 200px;
    overflow-y: auto;
  }

  .uploaded-files-grid {
    max-height: 200px;
    overflow-y: auto;
  }

  /* 文件勾选框样式 */
  .file-checkbox {
    position: absolute;
    top: 4px;
    left: 4px;
    z-index: 10;
    background: rgba(255, 255, 255, 0.9);
    border-radius: 2px;
    padding: 2px;
  }

  /* 图片hover预览样式 */
  .image-hover-preview {
    position: fixed;
    z-index: 9999;
    width: 300px;
    max-height: 400px;
    border: 3px solid #409eff;
    border-radius: 8px;
    overflow: hidden;
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4);
    background: white;
    pointer-events: none;
    padding: 10px;
  }

  .image-hover-preview img {
    width: 100%;
    height: auto;
    object-fit: contain;
    display: block;
    border-radius: 4px;
  }

  /* 优化文件名显示 */
  .file-name-small {
    font-size: 11px;
    color: #606266;
    text-align: center;
    margin-bottom: 5px;
    max-width: 100%;
    word-break: break-word;
    line-height: 1.2;
    max-height: 30px;
    overflow: hidden;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
  }
  </style>

  <style scoped>
  /* 存储管理样式 */
  .storage-management {
    margin-top: 15px;
    padding: 10px;
    border: 1px solid #EBEEF5;
    border-radius: 4px;
    background-color: #fafafa;
  }

  .storage-info {
    margin-bottom: 10px;
  }

  .storage-stats {
    display: flex;
    justify-content: space-between;
    align-items: center;
  }

  .storage-item {
    flex: 1;
    text-align: center;
  }

  .storage-value {
    font-weight: bold;
  }

  .storage-progress {
    margin-top: 5px;
  }

  .storage-actions {
    display: flex;
    justify-content: space-between;
    margin-top: 10px;
  }

  .storage-actions .el-button {
    flex: 1;
    margin-right: 10px;
  }

  .storage-actions .el-button:last-child {
    margin-right: 0;
  }

  /* AI默认参考素材样式 - 优化 */
  .node-references-section {
    margin-bottom: 10px;
    border: 1px solid #e4e7ed;
    border-radius: 4px;
    padding: 8px;
    background-color: #fafbfc;
  }

  .section-title {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 8px;
    font-size: 12px;
    color: #606266;
  }

  .section-title span {
    font-weight: 500;
  }

  .node-references-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(50px, 1fr));
    gap: 4px;
  }

  .reference-grid-item {
    position: relative;
    border: 2px solid transparent;
    border-radius: 4px;
    padding: 3px;
    cursor: pointer;
    transition: all 0.2s ease;
    background-color: white;
    text-align: center;
    overflow: hidden;
  }

  .reference-grid-item:hover {
    border-color: #409eff;
    box-shadow: 0 2px 8px rgba(64, 158, 255, 0.3);
    transform: scale(1.05);
  }

  .reference-grid-item.selected {
    border-color: #409eff;
    background-color: #ecf5ff;
    box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.2);
  }

  .reference-grid-item .file-preview {
    position: relative;
    width: 100%;
    height: 100%;
  }

  .reference-grid-item .image-thumbnail {
    width: 100%;
    height: 50px;
    margin: 0 auto;
  }

  .reference-grid-item .image-thumbnail img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    border-radius: 2px;
  }

  .reference-grid-item .file-icon-large {
    display: flex;
    justify-content: center;
    align-items: center;
    height: 50px;
    background-color: #f5f7fa;
    border-radius: 2px;
  }

  .reference-grid-item .file-icon-large i {
    font-size: 20px;
  }

  /* 弹窗中的参考素材样式 */
  .dialog-references-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(50px, 1fr));
    gap: 4px;
    max-height: 400px;
    overflow-y: auto;
    padding: 10px;
  }

  .dialog-reference-item {
    position: relative;
    border: 2px solid transparent;
    border-radius: 4px;
    padding: 3px;
    cursor: pointer;
    transition: all 0.2s ease;
    background-color: white;
    text-align: center;
    overflow: hidden;
  }

  .dialog-reference-item:hover {
    border-color: #409eff;
    box-shadow: 0 2px 8px rgba(64, 158, 255, 0.3);
    transform: scale(1.05);
  }

  .dialog-reference-item.selected {
    border-color: #409eff;
    background-color: #ecf5ff;
    box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.2);
  }

  .dialog-reference-item .file-preview {
    position: relative;
    width: 100%;
    height: 100%;
  }

  .dialog-reference-item .image-thumbnail {
    width: 100%;
    height: 50px;
    margin: 0 auto;
  }

  .dialog-reference-item .image-thumbnail img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    border-radius: 2px;
  }

  .dialog-reference-item .file-icon-large {
    display: flex;
    justify-content: center;
    align-items: center;
    height: 50px;
    background-color: #f5f7fa;
    border-radius: 2px;
  }

  .dialog-reference-item .file-icon-large i {
    font-size: 20px;
  }
  </style>

<style>
/* AI聊天页面树形节点选中状态 - 全局样式确保生效 */
.aisopchat-tree-selected .model-tree .el-tree-node.is-current > .el-tree-node__content {
  background: linear-gradient(135deg, #e3f2fd 0%, #bbdefb 100%) !important;
  color: #1565c0 !important;
  border-radius: 6px !important;
  position: relative !important;
  box-shadow: 0 2px 8px rgba(33, 150, 243, 0.2) !important;
  font-weight: 600 !important;
  transition: all 0.3s ease !important;
  border: 1px solid #90caf9 !important;
}

.aisopchat-tree-selected .model-tree .el-tree-node.is-current > .el-tree-node__content:hover {
  background: linear-gradient(135deg, #bbdefb 0%, #90caf9 100%) !important;
  color: #0d47a1 !important;
  box-shadow: 0 4px 12px rgba(33, 150, 243, 0.3) !important;
}

.aisopchat-tree-selected .model-tree .el-tree-node.is-current > .el-tree-node__content .el-tree-node__label {
  color: #1565c0 !important;
  font-weight: 600 !important;
}

.aisopchat-tree-selected .model-tree .el-tree-node.is-current > .el-tree-node__content .el-tree-node__expand-icon {
  color: #1976d2 !important;
}

.aisopchat-tree-selected .model-tree .el-tree-node.is-current > .el-tree-node__content::before {
  content: '';
  position: absolute;
  left: 0;
  top: 4px;
  bottom: 4px;
  width: 4px;
  background: #1976d2;
  border-radius: 0 2px 2px 0;
}

/* 🆕 连续对话输入框样式 */
.continuous-chat-input-area {
  border-top: 2px solid #e4e7ed;
  background: #f5f7fa;
  padding: 12px;
  flex-shrink: 0;
}

.continuous-input-header {
  display: flex;
  align-items: center;
  margin-bottom: 8px;
  font-size: 13px;
  color: #606266;
}

.continuous-input-header .el-icon-chat-dot-round {
  font-size: 16px;
  color: #409EFF;
  margin-right: 6px;
}

.input-mode-label {
  font-weight: 600;
  color: #409EFF;
}

.continuous-input-actions {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-top: 8px;
}

.continuous-input-actions .input-tip {
  font-size: 12px;
  color: #909399;
}

.continuous-input-actions .el-button {
  margin-left: auto;
}
</style>
